home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / md / windows / ntthread.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  12.2 KB  |  435 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  * 
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  * 
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. #include "primpl.h"
  20. #include <process.h>  /* for _beginthreadex() */
  21.  
  22. extern void _PR_Win32InitTimeZone(void);  /* defined in ntmisc.c */
  23.  
  24. /* --- globals ------------------------------------------------ */
  25. PRLock                       *_pr_schedLock = NULL;
  26. _PRInterruptTable             _pr_interruptTable[] = { { 0 } };
  27. __declspec(thread) PRThread  *_pr_current_fiber;
  28. __declspec(thread) PRThread  *_pr_fiber_last_run;
  29. __declspec(thread) _PRCPU    *_pr_current_cpu;
  30. __declspec(thread) PRUintn    _pr_ints_off;
  31.  
  32. _MDLock                       _nt_idleLock;
  33. PRCList                       _nt_idleList;
  34. PRUint32                        _nt_idleCount;
  35.  
  36. extern __declspec(thread) PRThread *_pr_io_restarted_io;
  37.  
  38. /* Must check the restarted_io *before* decrementing no_sched to 0 */
  39. #define POST_SWITCH_WORK() \
  40.     if (_pr_io_restarted_io) \
  41.         _nt_handle_restarted_io(_pr_io_restarted_io); \
  42.     _PR_MD_LAST_THREAD()->no_sched = 0;
  43.  
  44. void
  45. _nt_handle_restarted_io(PRThread *restarted_io)
  46. {
  47.     /* After the switch we can resume an IO if needed.
  48.      * XXXMB - this needs to be done in create thread, since that could
  49.      * be the result for a context switch too..
  50.      */
  51.     PR_ASSERT(restarted_io->io_suspended == PR_TRUE);
  52.  
  53.     _PR_THREAD_LOCK(restarted_io);
  54.     if (restarted_io->io_pending == PR_FALSE) {
  55.  
  56.         /* The IO already completed, put us back on the runq. */
  57.         int pri = restarted_io->priority;
  58.  
  59.         restarted_io->state = _PR_RUNNABLE;
  60.         _PR_RUNQ_LOCK(restarted_io->cpu);
  61.         _PR_ADD_RUNQ(restarted_io, restarted_io->cpu, pri);
  62.         _PR_RUNQ_UNLOCK(restarted_io->cpu);
  63.     } else {
  64.         _PR_SLEEPQ_LOCK(restarted_io->cpu);
  65.         _PR_ADD_SLEEPQ(restarted_io, restarted_io->sleep);
  66.         _PR_SLEEPQ_UNLOCK(restarted_io->cpu);
  67.     }
  68.     restarted_io->io_suspended = PR_FALSE;
  69.  
  70.     _PR_THREAD_UNLOCK(restarted_io);
  71.  
  72.     _pr_io_restarted_io = NULL;
  73. }
  74.  
  75. void
  76. _PR_MD_EARLY_INIT()
  77. {
  78.     _MD_NEW_LOCK( &_nt_idleLock );
  79.     _nt_idleCount = 0;
  80.     PR_INIT_CLIST(&_nt_idleList);
  81.     _PR_Win32InitTimeZone();
  82.  
  83. #if 0
  84.     /* Make the clock tick at least once per millisecond */
  85.     if ( timeBeginPeriod(1) == TIMERR_NOCANDO) {
  86.         /* deep yoghurt; clock doesn't tick fast enough! */
  87.         PR_ASSERT(0);
  88.     }
  89. #endif
  90. }
  91.  
  92. void _PR_MD_CLEANUP_BEFORE_EXIT(void)
  93. {
  94.     WSACleanup();
  95. }
  96.  
  97. void
  98. _PR_MD_INIT_PRIMORDIAL_THREAD(PRThread *thread)
  99. {
  100.     /*
  101.     ** Warning:
  102.     ** --------
  103.     ** NSPR requires a real handle to every thread.  GetCurrentThread()
  104.     ** returns a pseudo-handle which is not suitable for some thread
  105.     ** operations (ie. suspending).  Therefore, get a real handle from
  106.     ** the pseudo handle via DuplicateHandle(...)
  107.     */
  108.     DuplicateHandle( GetCurrentProcess(),     /* Process of source handle */
  109.                      GetCurrentThread(),      /* Pseudo Handle to dup */
  110.                      GetCurrentProcess(),     /* Process of handle */
  111.                      &(thread->md.handle),       /* resulting handle */
  112.                      0L,                      /* access flags */
  113.                      FALSE,                   /* Inheritable */
  114.                      DUPLICATE_SAME_ACCESS ); /* Options */
  115. }
  116.  
  117. PRStatus
  118. _PR_MD_INIT_THREAD(PRThread *thread)
  119. {
  120.     /* Create the blocking IO semaphore */
  121.     thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL);
  122.     if (thread->md.blocked_sema == NULL)
  123.         return PR_FAILURE;
  124.     else 
  125.         return PR_SUCCESS;
  126. }
  127.  
  128. PRStatus 
  129. _PR_MD_CREATE_THREAD(PRThread *thread, 
  130.                   void (*start)(void *), 
  131.                   PRThreadPriority priority, 
  132.                   PRThreadScope scope, 
  133.                   PRThreadState state, 
  134.                   PRUint32 stackSize)
  135. {
  136.  
  137. #if 0
  138.     thread->md.handle = CreateThread(
  139.                     NULL,                             /* security attrib */
  140.                     thread->stack->stackSize,         /* stack size      */
  141.                     (LPTHREAD_START_ROUTINE)start,    /* startup routine */
  142.                     (void *)thread,                   /* thread param    */
  143.                     CREATE_SUSPENDED,                 /* create flags    */
  144.                     &(thread->id) );                  /* thread id       */
  145. #else
  146.     thread->md.handle = (HANDLE) _beginthreadex(
  147.                     NULL,
  148.                     thread->stack->stackSize,
  149.                     (unsigned (__stdcall *)(void *))start,
  150.                     (void *)thread,
  151.                     CREATE_SUSPENDED,
  152.                     &(thread->id));
  153. #endif
  154.     if(!thread->md.handle) {
  155.         PRErrorCode prerror;
  156.         thread->md.fiber_last_error = GetLastError();
  157.         switch (errno) {
  158.             case ENOMEM:
  159.                 prerror = PR_OUT_OF_MEMORY_ERROR;
  160.                 break;
  161.             case EAGAIN:
  162.                 prerror = PR_INSUFFICIENT_RESOURCES_ERROR;
  163.                 break;
  164.             case EINVAL:
  165.                 prerror = PR_INVALID_ARGUMENT_ERROR;
  166.                 break;
  167.             default:
  168.                 prerror = PR_UNKNOWN_ERROR;
  169.         }
  170.         PR_SetError(prerror, errno);
  171.         return PR_FAILURE;
  172.     }
  173.  
  174.     thread->md.id = thread->id;
  175.  
  176.     /* Activate the thread */
  177.     if ( ResumeThread( thread->md.handle ) != -1)
  178.         return PR_SUCCESS;
  179.  
  180.     PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
  181.     return PR_FAILURE;
  182. }
  183.  
  184. void    
  185. _PR_MD_YIELD(void)
  186. {
  187.     /* Can NT really yield at all? */
  188.     Sleep(0);
  189. }
  190.  
  191. void     
  192. _PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
  193. {
  194. #if 0
  195.     /* XXXMB - does this work? Should we really set the priorities of
  196.      * native threads? */
  197.     if( newPri < 4 ) {
  198.         newPri = (PRUintn)THREAD_PRIORITY_IDLE;
  199.     } else if( newPri < 8 ) {
  200.         newPri = (PRUintn)THREAD_PRIORITY_LOWEST;
  201.     } else if( newPri < 12 ) {
  202.         newPri = (PRUintn)THREAD_PRIORITY_BELOW_NORMAL;
  203.     } else if( newPri < 16 ) {
  204.         newPri = (PRUintn)THREAD_PRIORITY_NORMAL;
  205.     } else if( newPri < 24 ) {
  206.         newPri = (PRUintn)THREAD_PRIORITY_ABOVE_NORMAL;
  207.     } else if( newPri < 28 ) {
  208.         newPri = (PRUintn)THREAD_PRIORITY_HIGHEST;
  209.     } else if( newPri < 32 ) {
  210.         newPri = (PRUintn)THREAD_PRIORITY_TIME_CRITICAL;
  211.     }        
  212.  
  213.     if( ! SetThreadPriority( thread->handle, newPri ) ) {
  214.         PR_LOG(_pr_thread_lm, PR_LOG_MIN,
  215.                ("PR_SetThreadPriority: can't set thread priority\n"));
  216.     }
  217. #endif
  218.  
  219.     return;
  220. }
  221.  
  222. void
  223. _PR_MD_CLEAN_THREAD(PRThread *thread)
  224. {
  225.     if (thread->md.acceptex_buf) {
  226.         PR_DELETE(thread->md.acceptex_buf);
  227.     }
  228.  
  229.     if (thread->md.xmit_bufs) {
  230.         PR_DELETE(thread->md.xmit_bufs);
  231.     }
  232.  
  233.     if (thread->md.blocked_sema) {
  234.         CloseHandle(thread->md.blocked_sema);
  235.         thread->md.blocked_sema = 0;
  236.     }
  237.  
  238.     if (thread->md.handle) {
  239.         CloseHandle(thread->md.handle);
  240.         thread->md.handle = 0;
  241.     }
  242.  
  243.     /* Don't call DeleteFiber on current fiber or we'll kill the whole thread.
  244.      * Don't call free(thread) until we've switched off the thread.
  245.      * So put this fiber (or thread) on a list to be deleted by the idle
  246.      * fiber next time we have a chance.
  247.      */
  248.     if (!(thread->flags & (_PR_ATTACHED|_PR_GLOBAL_SCOPE))) {
  249.         _MD_LOCK(&_nt_idleLock);
  250.         _nt_idleCount++;
  251.         PR_APPEND_LINK(&thread->links, &_nt_idleList);
  252.         _MD_UNLOCK(&_nt_idleLock);
  253.     }
  254. }
  255.  
  256. void
  257. _PR_MD_EXIT_THREAD(PRThread *thread)
  258. {
  259.     if (thread->md.acceptex_buf) {
  260.         PR_DELETE(thread->md.acceptex_buf);
  261.     }
  262.  
  263.     if (thread->md.xmit_bufs) {
  264.         PR_DELETE(thread->md.xmit_bufs);
  265.     }
  266.  
  267.     if (thread->md.blocked_sema) {
  268.         CloseHandle(thread->md.blocked_sema);
  269.         thread->md.blocked_sema = 0;
  270.     }
  271.  
  272.     if (thread->md.handle) {
  273.         CloseHandle(thread->md.handle);
  274.         thread->md.handle = 0;
  275.     }
  276.  
  277.     if (thread->flags & _PR_GLOBAL_SCOPE) {
  278.         _MD_SET_CURRENT_THREAD(NULL);
  279.     }
  280. }
  281.  
  282.  
  283. void
  284. _PR_MD_EXIT(PRIntn status)
  285. {
  286.     _exit(status);
  287. }
  288.  
  289. #ifdef HAVE_FIBERS
  290.  
  291. void
  292. _pr_fiber_mainline(void *unused) 
  293. {
  294.     PRThread *fiber = _PR_MD_CURRENT_THREAD();
  295.  
  296.     POST_SWITCH_WORK();
  297.  
  298.     fiber->md.fiber_fn(fiber->md.fiber_arg);
  299. }
  300.  
  301. PRThread *_PR_MD_CREATE_USER_THREAD(
  302.     PRUint32 stacksize, void (*start)(void *), void *arg)
  303. {
  304.     PRThread *thread;
  305.  
  306.     if ( (thread = PR_NEW(PRThread)) == NULL ) {
  307.         return NULL;
  308.     }
  309.     
  310.     memset(thread, 0, sizeof(PRThread));
  311.     thread->md.fiber_fn = start;
  312.     thread->md.fiber_arg = arg;
  313.     thread->md.fiber_stacksize = stacksize;
  314.     return thread;
  315. }
  316.  
  317. void
  318. _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(PRThread *thread)
  319. {
  320.     thread->md.fiber_id = ConvertThreadToFiber(NULL);
  321.     PR_ASSERT(thread->md.fiber_id);
  322.     thread->flags &= (~_PR_GLOBAL_SCOPE);
  323.     _MD_SET_CURRENT_THREAD(thread);
  324.     _MD_SET_LAST_THREAD(thread);
  325.     thread->no_sched = 1;
  326.     return;
  327. }
  328.  
  329. void
  330. _PR_MD_INIT_CONTEXT(PRThread *thread, char *top, void (*start) (void), PRBool *status)
  331. {
  332.     thread->md.fiber_fn = (void (*)(void *))start;
  333.     thread->md.fiber_id = CreateFiber(thread->md.fiber_stacksize, 
  334.         (LPFIBER_START_ROUTINE)_pr_fiber_mainline, NULL);
  335.     if (thread->md.fiber_id != 0)
  336.         *status = PR_TRUE;
  337.     else {
  338.         DWORD oserror = GetLastError();
  339.         PRErrorCode prerror;
  340.         if (oserror == ERROR_NOT_ENOUGH_MEMORY) {
  341.             prerror = PR_OUT_OF_MEMORY_ERROR;
  342.         } else {
  343.             prerror = PR_UNKNOWN_ERROR;
  344.         }
  345.         PR_SetError(prerror, oserror);
  346.         *status = PR_FALSE;
  347.     }
  348. }
  349.  
  350. void
  351. _PR_MD_SWITCH_CONTEXT(PRThread *thread)
  352. {
  353.     PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
  354.  
  355.     thread->md.fiber_last_error = GetLastError();
  356.     _PR_Schedule();
  357. }
  358.  
  359. void
  360. _PR_MD_RESTORE_CONTEXT(PRThread *thread)
  361. {
  362.     PRThread *me = _PR_MD_CURRENT_THREAD();
  363.  
  364.     PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
  365.  
  366.     /* The user-level code for yielding will happily add ourselves to the runq
  367.      * and then switch to ourselves; the NT fibers can't handle switching to 
  368.      * ourselves.
  369.      */
  370.     if (thread != me) {
  371.         SetLastError(thread->md.fiber_last_error);
  372.         _MD_SET_CURRENT_THREAD(thread);
  373.         _PR_MD_SET_LAST_THREAD(me);
  374.         thread->no_sched = 1;
  375.         SwitchToFiber(thread->md.fiber_id);
  376.         POST_SWITCH_WORK();
  377.     }
  378. }
  379.  
  380.  
  381. #endif /* HAVE_FIBERS */
  382.  
  383. PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
  384. {
  385.     int rv;
  386.  
  387.     rv = SetThreadAffinityMask(thread->md.handle, mask);
  388.  
  389.     return rv?0:-1;
  390. }
  391.  
  392. PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
  393. {
  394.     PRInt32 rv, system_mask;
  395.  
  396.     rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask);
  397.     
  398.     return rv?0:-1;
  399. }
  400.  
  401. void 
  402. _PR_MD_SUSPEND_CPU(_PRCPU *cpu) 
  403. {
  404.     _PR_MD_SUSPEND_THREAD(cpu->thread);
  405. }
  406.  
  407. void
  408. _PR_MD_RESUME_CPU(_PRCPU *cpu)
  409. {
  410.     _PR_MD_RESUME_THREAD(cpu->thread);
  411. }
  412.  
  413. void
  414. _PR_MD_SUSPEND_THREAD(PRThread *thread)
  415. {
  416.     if (_PR_IS_NATIVE_THREAD(thread)) {
  417.         /*
  418.         ** There seems to be some doubt about whether or not SuspendThread
  419.         ** is a synchronous function. The test afterwards is to help veriry
  420.         ** that it is, which is what Microsoft says it is.
  421.         */
  422.         PRUintn rv = SuspendThread(thread->md.handle);
  423.         PR_ASSERT(0xffffffffUL != rv);
  424.     }
  425. }
  426.  
  427. void
  428. _PR_MD_RESUME_THREAD(PRThread *thread)
  429. {
  430.     if (_PR_IS_NATIVE_THREAD(thread)) {
  431.         ResumeThread(thread->md.handle);
  432.     }
  433. }
  434.  
  435.