Back to index

lightning-sunbird  0.9+nobinonly
ntthread.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "primpl.h"
00039 #include <process.h>  /* for _beginthreadex() */
00040 
00041 /* --- globals ------------------------------------------------ */
00042 PRLock                       *_pr_schedLock = NULL;
00043 _PRInterruptTable             _pr_interruptTable[] = { { 0 } };
00044 
00045 BOOL _pr_use_static_tls = TRUE;
00046 __declspec(thread) PRThread  *_pr_current_fiber;
00047 __declspec(thread) PRThread  *_pr_fiber_last_run;
00048 __declspec(thread) _PRCPU    *_pr_current_cpu;
00049 __declspec(thread) PRUintn    _pr_ints_off;
00050 DWORD _pr_currentFiberIndex;
00051 DWORD _pr_lastFiberIndex;
00052 DWORD _pr_currentCPUIndex;
00053 DWORD _pr_intsOffIndex;
00054 
00055 _MDLock                       _nt_idleLock;
00056 PRCList                       _nt_idleList;
00057 PRUint32                        _nt_idleCount;
00058 
00059 extern __declspec(thread) PRThread *_pr_io_restarted_io;
00060 extern DWORD _pr_io_restartedIOIndex;
00061 
00062 /* Must check the restarted_io *before* decrementing no_sched to 0 */
00063 #define POST_SWITCH_WORK() \
00064     PR_BEGIN_MACRO \
00065         PRThread *restarted_io = \
00066             (_pr_use_static_tls ? _pr_io_restarted_io \
00067             : (PRThread *) TlsGetValue(_pr_io_restartedIOIndex)); \
00068         if (restarted_io) { \
00069             _nt_handle_restarted_io(restarted_io); \
00070         } \
00071         _PR_MD_LAST_THREAD()->no_sched = 0; \
00072     PR_END_MACRO
00073 
00074 void
00075 _nt_handle_restarted_io(PRThread *restarted_io)
00076 {
00077     /* After the switch we can resume an IO if needed.
00078      * XXXMB - this needs to be done in create thread, since that could
00079      * be the result for a context switch too..
00080      */
00081     PR_ASSERT(restarted_io->io_suspended == PR_TRUE);
00082     PR_ASSERT(restarted_io->md.thr_bound_cpu == restarted_io->cpu);
00083 
00084     _PR_THREAD_LOCK(restarted_io);
00085     if (restarted_io->io_pending == PR_FALSE) {
00086 
00087         /* The IO already completed, put us back on the runq. */
00088         int pri = restarted_io->priority;
00089 
00090         restarted_io->state = _PR_RUNNABLE;
00091         _PR_RUNQ_LOCK(restarted_io->cpu);
00092         _PR_ADD_RUNQ(restarted_io, restarted_io->cpu, pri);
00093         _PR_RUNQ_UNLOCK(restarted_io->cpu);
00094     } else {
00095         _PR_SLEEPQ_LOCK(restarted_io->cpu);
00096         _PR_ADD_SLEEPQ(restarted_io, restarted_io->sleep);
00097         _PR_SLEEPQ_UNLOCK(restarted_io->cpu);
00098     }
00099     restarted_io->io_suspended = PR_FALSE;
00100     restarted_io->md.thr_bound_cpu = NULL;
00101 
00102     _PR_THREAD_UNLOCK(restarted_io);
00103 
00104     if (_pr_use_static_tls) {
00105         _pr_io_restarted_io = NULL;
00106     } else {
00107         TlsSetValue(_pr_io_restartedIOIndex, NULL);
00108     }
00109 }
00110 
00111 void
00112 _PR_MD_EARLY_INIT()
00113 {
00114     _MD_NEW_LOCK( &_nt_idleLock );
00115     _nt_idleCount = 0;
00116     PR_INIT_CLIST(&_nt_idleList);
00117 
00118 #if 0
00119     /* Make the clock tick at least once per millisecond */
00120     if ( timeBeginPeriod(1) == TIMERR_NOCANDO) {
00121         /* deep yoghurt; clock doesn't tick fast enough! */
00122         PR_ASSERT(0);
00123     }
00124 #endif
00125 
00126     if (!_pr_use_static_tls) {
00127         _pr_currentFiberIndex = TlsAlloc();
00128         _pr_lastFiberIndex = TlsAlloc();
00129         _pr_currentCPUIndex = TlsAlloc();
00130         _pr_intsOffIndex = TlsAlloc();
00131         _pr_io_restartedIOIndex = TlsAlloc();
00132     }
00133 }
00134 
00135 void _PR_MD_CLEANUP_BEFORE_EXIT(void)
00136 {
00137     _PR_NT_FreeSids();
00138 
00139     WSACleanup();
00140 
00141     if (!_pr_use_static_tls) {
00142         TlsFree(_pr_currentFiberIndex);
00143         TlsFree(_pr_lastFiberIndex);
00144         TlsFree(_pr_currentCPUIndex);
00145         TlsFree(_pr_intsOffIndex);
00146         TlsFree(_pr_io_restartedIOIndex);
00147     }
00148 }
00149 
00150 PRStatus
00151 _PR_MD_INIT_THREAD(PRThread *thread)
00152 {
00153     thread->md.overlapped.ioModel = _MD_BlockingIO;
00154     thread->md.overlapped.data.mdThread = &thread->md;
00155 
00156     if (thread->flags & _PR_GLOBAL_SCOPE) {
00157         if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) {
00158             /*
00159             ** Warning:
00160             ** --------
00161             ** NSPR requires a real handle to every thread.
00162             ** GetCurrentThread() returns a pseudo-handle which
00163             ** is not suitable for some thread operations (e.g.,
00164             ** suspending).  Therefore, get a real handle from
00165             ** the pseudo handle via DuplicateHandle(...)
00166             */
00167             DuplicateHandle(
00168                     GetCurrentProcess(),     /* Process of source handle */
00169                     GetCurrentThread(),      /* Pseudo Handle to dup */
00170                     GetCurrentProcess(),     /* Process of handle */
00171                     &(thread->md.handle),    /* resulting handle */
00172                     0L,                      /* access flags */
00173                     FALSE,                   /* Inheritable */
00174                     DUPLICATE_SAME_ACCESS);  /* Options */
00175         }
00176 
00177         /* Create the blocking IO semaphore */
00178         thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL);
00179         if (thread->md.blocked_sema == NULL) {
00180             return PR_FAILURE;
00181         }
00182               if (_native_threads_only) {
00183                      /* Create the blocking IO semaphore */
00184                      thread->md.thr_event = CreateEvent(NULL, TRUE, FALSE, NULL);
00185                      if (thread->md.thr_event == NULL) {
00186                             return PR_FAILURE;
00187                      }
00188               }
00189     }
00190 
00191     return PR_SUCCESS;
00192 }
00193 
00194 static unsigned __stdcall
00195 pr_root(void *arg)
00196 {
00197     PRThread *thread = (PRThread *)arg;
00198     thread->md.start(thread);
00199     return 0;
00200 }
00201 
00202 PRStatus 
00203 _PR_MD_CREATE_THREAD(PRThread *thread, 
00204                   void (*start)(void *), 
00205                   PRThreadPriority priority, 
00206                   PRThreadScope scope, 
00207                   PRThreadState state, 
00208                   PRUint32 stackSize)
00209 {
00210 
00211     thread->md.start = start;
00212     thread->md.handle = (HANDLE) _beginthreadex(
00213                     NULL,
00214                     thread->stack->stackSize,
00215                     pr_root,
00216                     (void *)thread,
00217                     CREATE_SUSPENDED,
00218                     &(thread->id));
00219     if(!thread->md.handle) {
00220         PRErrorCode prerror;
00221         thread->md.fiber_last_error = GetLastError();
00222         switch (errno) {
00223             case ENOMEM:
00224                 prerror = PR_OUT_OF_MEMORY_ERROR;
00225                 break;
00226             case EAGAIN:
00227                 prerror = PR_INSUFFICIENT_RESOURCES_ERROR;
00228                 break;
00229             case EINVAL:
00230                 prerror = PR_INVALID_ARGUMENT_ERROR;
00231                 break;
00232             default:
00233                 prerror = PR_UNKNOWN_ERROR;
00234         }
00235         PR_SetError(prerror, errno);
00236         return PR_FAILURE;
00237     }
00238 
00239     thread->md.id = thread->id;
00240     /*
00241      * On windows, a thread is created with a thread priority of
00242      * THREAD_PRIORITY_NORMAL.
00243      */
00244     if (priority != PR_PRIORITY_NORMAL) {
00245         _PR_MD_SET_PRIORITY(&(thread->md), priority);
00246     }
00247 
00248     /* Activate the thread */
00249     if ( ResumeThread( thread->md.handle ) != -1)
00250         return PR_SUCCESS;
00251 
00252     PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
00253     return PR_FAILURE;
00254 }
00255 
00256 void
00257 _PR_MD_JOIN_THREAD(_MDThread *md)
00258 {
00259     DWORD rv;
00260 
00261     rv = WaitForSingleObject(md->handle, INFINITE);
00262     PR_ASSERT(WAIT_OBJECT_0 == rv);
00263 }
00264 
00265 void
00266 _PR_MD_END_THREAD(void)
00267 {
00268     _endthreadex(0);
00269 }
00270 
00271 void    
00272 _PR_MD_YIELD(void)
00273 {
00274     /* Can NT really yield at all? */
00275     Sleep(0);
00276 }
00277 
00278 void     
00279 _PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
00280 {
00281     int nativePri;
00282     BOOL rv;
00283 
00284     if (newPri < PR_PRIORITY_FIRST) {
00285         newPri = PR_PRIORITY_FIRST;
00286     } else if (newPri > PR_PRIORITY_LAST) {
00287         newPri = PR_PRIORITY_LAST;
00288     }
00289     switch (newPri) {
00290         case PR_PRIORITY_LOW:
00291             nativePri = THREAD_PRIORITY_BELOW_NORMAL;
00292             break;
00293         case PR_PRIORITY_NORMAL:
00294             nativePri = THREAD_PRIORITY_NORMAL;
00295             break;
00296         case PR_PRIORITY_HIGH:
00297             nativePri = THREAD_PRIORITY_ABOVE_NORMAL;
00298             break;
00299         case PR_PRIORITY_URGENT:
00300             nativePri = THREAD_PRIORITY_HIGHEST;
00301     }
00302     rv = SetThreadPriority(thread->handle, nativePri);
00303     PR_ASSERT(rv);
00304     if (!rv) {
00305        PR_LOG(_pr_thread_lm, PR_LOG_MIN,
00306                 ("PR_SetThreadPriority: can't set thread priority\n"));
00307     }
00308     return;
00309 }
00310 
00311 void
00312 _PR_MD_CLEAN_THREAD(PRThread *thread)
00313 {
00314     BOOL rv;
00315 
00316     if (thread->md.acceptex_buf) {
00317         PR_DELETE(thread->md.acceptex_buf);
00318     }
00319 
00320     if (thread->md.xmit_bufs) {
00321         PR_DELETE(thread->md.xmit_bufs);
00322     }
00323 
00324     if (thread->md.blocked_sema) {
00325         rv = CloseHandle(thread->md.blocked_sema);
00326         PR_ASSERT(rv);
00327         thread->md.blocked_sema = 0;
00328     }
00329        if (_native_threads_only) {
00330               if (thread->md.thr_event) {
00331                      rv = CloseHandle(thread->md.thr_event);
00332                      PR_ASSERT(rv);
00333                      thread->md.thr_event = 0;
00334               }
00335        }
00336 
00337     if (thread->md.handle) {
00338         rv = CloseHandle(thread->md.handle);
00339         PR_ASSERT(rv);
00340         thread->md.handle = 0;
00341     }
00342 
00343     /* Don't call DeleteFiber on current fiber or we'll kill the whole thread.
00344      * Don't call free(thread) until we've switched off the thread.
00345      * So put this fiber (or thread) on a list to be deleted by the idle
00346      * fiber next time we have a chance.
00347      */
00348     if (!(thread->flags & (_PR_ATTACHED|_PR_GLOBAL_SCOPE))) {
00349         _MD_LOCK(&_nt_idleLock);
00350         _nt_idleCount++;
00351         PR_APPEND_LINK(&thread->links, &_nt_idleList);
00352         _MD_UNLOCK(&_nt_idleLock);
00353     }
00354 }
00355 
00356 void
00357 _PR_MD_EXIT_THREAD(PRThread *thread)
00358 {
00359     BOOL rv;
00360 
00361     if (thread->md.acceptex_buf) {
00362         PR_DELETE(thread->md.acceptex_buf);
00363     }
00364 
00365     if (thread->md.xmit_bufs) {
00366         PR_DELETE(thread->md.xmit_bufs);
00367     }
00368 
00369     if (thread->md.blocked_sema) {
00370         rv = CloseHandle(thread->md.blocked_sema);
00371         PR_ASSERT(rv);
00372         thread->md.blocked_sema = 0;
00373     }
00374 
00375        if (_native_threads_only) {
00376               if (thread->md.thr_event) {
00377                      rv = CloseHandle(thread->md.thr_event);
00378                      PR_ASSERT(rv);
00379                      thread->md.thr_event = 0;
00380               }
00381        }
00382 
00383     if (thread->md.handle) {
00384         rv = CloseHandle(thread->md.handle);
00385         PR_ASSERT(rv);
00386         thread->md.handle = 0;
00387     }
00388 
00389     if (thread->flags & _PR_GLOBAL_SCOPE) {
00390         _MD_SET_CURRENT_THREAD(NULL);
00391     }
00392 }
00393 
00394 
00395 void
00396 _PR_MD_EXIT(PRIntn status)
00397 {
00398     _exit(status);
00399 }
00400 
00401 #ifdef HAVE_FIBERS
00402 
00403 void
00404 _pr_fiber_mainline(void *unused) 
00405 {
00406     PRThread *fiber = _PR_MD_CURRENT_THREAD();
00407 
00408     POST_SWITCH_WORK();
00409 
00410     fiber->md.fiber_fn(fiber->md.fiber_arg);
00411 }
00412 
00413 PRThread *_PR_MD_CREATE_USER_THREAD(
00414     PRUint32 stacksize, void (*start)(void *), void *arg)
00415 {
00416     PRThread *thread;
00417 
00418     if ( (thread = PR_NEW(PRThread)) == NULL ) {
00419         return NULL;
00420     }
00421     
00422     memset(thread, 0, sizeof(PRThread));
00423     thread->md.fiber_fn = start;
00424     thread->md.fiber_arg = arg;
00425     thread->md.fiber_stacksize = stacksize;
00426     return thread;
00427 }
00428 
00429 void
00430 _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(PRThread *thread)
00431 {
00432     thread->md.fiber_id = ConvertThreadToFiber(NULL);
00433     PR_ASSERT(thread->md.fiber_id);
00434     _MD_SET_CURRENT_THREAD(thread);
00435     _MD_SET_LAST_THREAD(thread);
00436     thread->no_sched = 1;
00437     return;
00438 }
00439 
00440 void
00441 _PR_MD_INIT_CONTEXT(PRThread *thread, char *top, void (*start) (void), PRBool *status)
00442 {
00443     thread->md.fiber_fn = (void (*)(void *))start;
00444     thread->md.fiber_id = CreateFiber(thread->md.fiber_stacksize, 
00445         (LPFIBER_START_ROUTINE)_pr_fiber_mainline, NULL);
00446     if (thread->md.fiber_id != 0)
00447         *status = PR_TRUE;
00448     else {
00449         DWORD oserror = GetLastError();
00450         PRErrorCode prerror;
00451         if (oserror == ERROR_NOT_ENOUGH_MEMORY) {
00452             prerror = PR_OUT_OF_MEMORY_ERROR;
00453         } else {
00454             prerror = PR_UNKNOWN_ERROR;
00455         }
00456         PR_SetError(prerror, oserror);
00457         *status = PR_FALSE;
00458     }
00459 }
00460 
00461 void
00462 _PR_MD_SWITCH_CONTEXT(PRThread *thread)
00463 {
00464     PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
00465 
00466     thread->md.fiber_last_error = GetLastError();
00467     _PR_Schedule();
00468 }
00469 
00470 void
00471 _PR_MD_RESTORE_CONTEXT(PRThread *thread)
00472 {
00473     PRThread *me = _PR_MD_CURRENT_THREAD();
00474 
00475     PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
00476 
00477     /* The user-level code for yielding will happily add ourselves to the runq
00478      * and then switch to ourselves; the NT fibers can't handle switching to 
00479      * ourselves.
00480      */
00481     if (thread != me) {
00482         SetLastError(thread->md.fiber_last_error);
00483         _MD_SET_CURRENT_THREAD(thread);
00484         _PR_MD_SET_LAST_THREAD(me);
00485         thread->no_sched = 1;
00486         SwitchToFiber(thread->md.fiber_id);
00487         POST_SWITCH_WORK();
00488     }
00489 }
00490 
00491 
00492 #endif /* HAVE_FIBERS */
00493 
00494 PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
00495 {
00496     int rv;
00497 
00498     rv = SetThreadAffinityMask(thread->md.handle, mask);
00499 
00500     return rv?0:-1;
00501 }
00502 
00503 PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
00504 {
00505     PRInt32 rv, system_mask;
00506 
00507     rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask);
00508     
00509     return rv?0:-1;
00510 }
00511 
00512 void 
00513 _PR_MD_SUSPEND_CPU(_PRCPU *cpu) 
00514 {
00515     _PR_MD_SUSPEND_THREAD(cpu->thread);
00516 }
00517 
00518 void
00519 _PR_MD_RESUME_CPU(_PRCPU *cpu)
00520 {
00521     _PR_MD_RESUME_THREAD(cpu->thread);
00522 }
00523 
00524 void
00525 _PR_MD_SUSPEND_THREAD(PRThread *thread)
00526 {
00527     if (_PR_IS_NATIVE_THREAD(thread)) {
00528         /*
00529         ** There seems to be some doubt about whether or not SuspendThread
00530         ** is a synchronous function. The test afterwards is to help veriry
00531         ** that it is, which is what Microsoft says it is.
00532         */
00533         PRUintn rv = SuspendThread(thread->md.handle);
00534         PR_ASSERT(0xffffffffUL != rv);
00535     }
00536 }
00537 
00538 void
00539 _PR_MD_RESUME_THREAD(PRThread *thread)
00540 {
00541     if (_PR_IS_NATIVE_THREAD(thread)) {
00542         ResumeThread(thread->md.handle);
00543     }
00544 }
00545 
00546 PRThread*
00547 _MD_CURRENT_THREAD(void)
00548 {
00549 PRThread *thread;
00550 
00551        thread = _MD_GET_ATTACHED_THREAD();
00552 
00553        if (NULL == thread) {
00554               thread = _PRI_AttachThread(
00555             PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
00556        }
00557        PR_ASSERT(thread != NULL);
00558        return thread;
00559 }
00560