home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 15 / AACD15.ISO / AACD / Programming / Python2 / Python20_source / Python / thread_nt.h < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-25  |  8.9 KB  |  333 lines

  1.  
  2. /* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */
  3. /* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */
  4.  
  5. #include <windows.h>
  6. #include <limits.h>
  7. #include <process.h>
  8.  
  9. typedef struct NRMUTEX {
  10.     LONG   owned ;
  11.     DWORD  thread_id ;
  12.     HANDLE hevent ;
  13. } NRMUTEX, *PNRMUTEX ;
  14.  
  15.  
  16. typedef PVOID WINAPI interlocked_cmp_xchg_t(PVOID *dest, PVOID exc, PVOID comperand) ;
  17.  
  18. /* Sorry mate, but we haven't got InterlockedCompareExchange in Win95! */
  19. static PVOID WINAPI interlocked_cmp_xchg(PVOID *dest, PVOID exc, PVOID comperand)
  20. {
  21.     static LONG spinlock = 0 ;
  22.     PVOID result ;
  23.     DWORD dwSleep = 0;
  24.  
  25.     /* Acqire spinlock (yielding control to other threads if cant aquire for the moment) */
  26.     while(InterlockedExchange(&spinlock, 1))
  27.     {
  28.         // Using Sleep(0) can cause a priority inversion.
  29.         // Sleep(0) only yields the processor if there's
  30.         // another thread of the same priority that's
  31.         // ready to run.  If a high-priority thread is
  32.         // trying to acquire the lock, which is held by
  33.         // a low-priority thread, then the low-priority
  34.         // thread may never get scheduled and hence never
  35.         // free the lock.  NT attempts to avoid priority
  36.         // inversions by temporarily boosting the priority
  37.         // of low-priority runnable threads, but the problem
  38.         // can still occur if there's a medium-priority
  39.         // thread that's always runnable.  If Sleep(1) is used,
  40.         // then the thread unconditionally yields the CPU.  We
  41.         // only do this for the second and subsequent even
  42.         // iterations, since a millisecond is a long time to wait
  43.         // if the thread can be scheduled in again sooner
  44.         // (~100,000 instructions).
  45.         // Avoid priority inversion: 0, 1, 0, 1,...
  46.         Sleep(dwSleep);
  47.         dwSleep = !dwSleep;
  48.     }
  49.     result = *dest ;
  50.     if (result == comperand)
  51.         *dest = exc ;
  52.     /* Release spinlock */
  53.     spinlock = 0 ;
  54.     return result ;
  55. } ;
  56.  
  57. static interlocked_cmp_xchg_t *ixchg ;
  58. BOOL InitializeNonRecursiveMutex(PNRMUTEX mutex)
  59. {
  60.     if (!ixchg)
  61.     {
  62.         /* Sorely, Win95 has no InterlockedCompareExchange API (Win98 has), so we have to use emulation */
  63.         HANDLE kernel = GetModuleHandle("kernel32.dll") ;
  64.         if (!kernel || (ixchg = (interlocked_cmp_xchg_t *)GetProcAddress(kernel, "InterlockedCompareExchange")) == NULL)
  65.             ixchg = interlocked_cmp_xchg ;
  66.     }
  67.  
  68.     mutex->owned = -1 ;  /* No threads have entered NonRecursiveMutex */
  69.     mutex->thread_id = 0 ;
  70.     mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
  71.     return mutex->hevent != NULL ;    /* TRUE if the mutex is created */
  72. }
  73.  
  74. #ifdef InterlockedCompareExchange
  75. #undef InterlockedCompareExchange
  76. #endif
  77. #define InterlockedCompareExchange(dest,exchange,comperand) (ixchg((dest), (exchange), (comperand)))
  78.  
  79. VOID DeleteNonRecursiveMutex(PNRMUTEX mutex)
  80. {
  81.     /* No in-use check */
  82.     CloseHandle(mutex->hevent) ;
  83.     mutex->hevent = NULL ; /* Just in case */
  84. }
  85.  
  86. DWORD EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
  87. {
  88.     /* Assume that the thread waits successfully */
  89.     DWORD ret ;
  90.  
  91.     /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
  92.     if (!wait)
  93.     {
  94.         if (InterlockedCompareExchange((PVOID *)&mutex->owned, (PVOID)0, (PVOID)-1) != (PVOID)-1)
  95.             return WAIT_TIMEOUT ;
  96.         ret = WAIT_OBJECT_0 ;
  97.     }
  98.     else
  99.         ret = InterlockedIncrement(&mutex->owned) ?
  100.             /* Some thread owns the mutex, let's wait... */
  101.             WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ;
  102.  
  103.     mutex->thread_id = GetCurrentThreadId() ; /* We own it */
  104.     return ret ;
  105. }
  106.  
  107. BOOL LeaveNonRecursiveMutex(PNRMUTEX mutex)
  108. {
  109.     /* We don't own the mutex */
  110.     mutex->thread_id = 0 ;
  111.     return
  112.         InterlockedDecrement(&mutex->owned) < 0 ||
  113.         SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */
  114. }
  115.  
  116. PNRMUTEX AllocNonRecursiveMutex(void)
  117. {
  118.     PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ;
  119.     if (mutex && !InitializeNonRecursiveMutex(mutex))
  120.     {
  121.         free(mutex) ;
  122.         mutex = NULL ;
  123.     }
  124.     return mutex ;
  125. }
  126.  
  127. void FreeNonRecursiveMutex(PNRMUTEX mutex)
  128. {
  129.     if (mutex)
  130.     {
  131.         DeleteNonRecursiveMutex(mutex) ;
  132.         free(mutex) ;
  133.     }
  134. }
  135.  
  136. long PyThread_get_thread_ident(void);
  137.  
  138. /*
  139.  * Change all headers to pure ANSI as no one will use K&R style on an
  140.  * NT
  141.  */
  142.  
  143. /*
  144.  * Initialization of the C package, should not be needed.
  145.  */
  146. static void PyThread__init_thread(void)
  147. {
  148. }
  149.  
  150. /*
  151.  * Thread support.
  152.  */
  153. int PyThread_start_new_thread(void (*func)(void *), void *arg)
  154. {
  155.     uintptr_t rv;
  156.     int success = 0;
  157.  
  158.     dprintf(("%ld: PyThread_start_new_thread called\n", PyThread_get_thread_ident()));
  159.     if (!initialized)
  160.         PyThread_init_thread();
  161.  
  162.     rv = _beginthread(func, 0, arg); /* use default stack size */
  163.  
  164.     if (rv != -1) {
  165.         success = 1;
  166.         dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n", PyThread_get_thread_ident(), rv));
  167.     }
  168.  
  169.     return success;
  170. }
  171.  
  172. /*
  173.  * Return the thread Id instead of an handle. The Id is said to uniquely identify the
  174.  * thread in the system
  175.  */
  176. long PyThread_get_thread_ident(void)
  177. {
  178.     if (!initialized)
  179.         PyThread_init_thread();
  180.  
  181.     return GetCurrentThreadId();
  182. }
  183.  
  184. static void do_PyThread_exit_thread(int no_cleanup)
  185. {
  186.     dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident()));
  187.     if (!initialized)
  188.         if (no_cleanup)
  189.             _exit(0);
  190.         else
  191.             exit(0);
  192.     _endthread();
  193. }
  194.  
  195. void PyThread_exit_thread(void)
  196. {
  197.     do_PyThread_exit_thread(0);
  198. }
  199.  
  200. void PyThread__exit_thread(void)
  201. {
  202.     do_PyThread_exit_thread(1);
  203. }
  204.  
  205. #ifndef NO_EXIT_PROG
  206. static void do_PyThread_exit_prog(int status, int no_cleanup)
  207. {
  208.     dprintf(("PyThread_exit_prog(%d) called\n", status));
  209.     if (!initialized)
  210.         if (no_cleanup)
  211.             _exit(status);
  212.         else
  213.             exit(status);
  214. }
  215.  
  216. void PyThread_exit_prog(int status)
  217. {
  218.     do_PyThread_exit_prog(status, 0);
  219. }
  220.  
  221. void PyThread__exit_prog(int status)
  222. {
  223.     do_PyThread_exit_prog(status, 1);
  224. }
  225. #endif /* NO_EXIT_PROG */
  226.  
  227. /*
  228.  * Lock support. It has too be implemented as semaphores.
  229.  * I [Dag] tried to implement it with mutex but I could find a way to
  230.  * tell whether a thread already own the lock or not.
  231.  */
  232. PyThread_type_lock PyThread_allocate_lock(void)
  233. {
  234.     PNRMUTEX aLock;
  235.  
  236.     dprintf(("PyThread_allocate_lock called\n"));
  237.     if (!initialized)
  238.         PyThread_init_thread();
  239.  
  240.     aLock = AllocNonRecursiveMutex() ;
  241.  
  242.     dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock));
  243.  
  244.     return (PyThread_type_lock) aLock;
  245. }
  246.  
  247. void PyThread_free_lock(PyThread_type_lock aLock)
  248. {
  249.     dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
  250.  
  251.     FreeNonRecursiveMutex(aLock) ;
  252. }
  253.  
  254. /*
  255.  * Return 1 on success if the lock was acquired
  256.  *
  257.  * and 0 if the lock was not acquired. This means a 0 is returned
  258.  * if the lock has already been acquired by this thread!
  259.  */
  260. int PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
  261. {
  262.     int success ;
  263.  
  264.     dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag));
  265.  
  266.     success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag == 1 ? INFINITE : 0)) == WAIT_OBJECT_0 ;
  267.  
  268.     dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success));
  269.  
  270.     return success;
  271. }
  272.  
  273. void PyThread_release_lock(PyThread_type_lock aLock)
  274. {
  275.     dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
  276.  
  277.     if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock)))
  278.         dprintf(("%ld: Could not PyThread_release_lock(%p) error: %l\n", PyThread_get_thread_ident(), aLock, GetLastError()));
  279. }
  280.  
  281. /*
  282.  * Semaphore support.
  283.  */
  284. PyThread_type_sema PyThread_allocate_sema(int value)
  285. {
  286.     HANDLE aSemaphore;
  287.  
  288.     dprintf(("%ld: PyThread_allocate_sema called\n", PyThread_get_thread_ident()));
  289.     if (!initialized)
  290.         PyThread_init_thread();
  291.  
  292.     aSemaphore = CreateSemaphore( NULL,           /* Security attributes          */
  293.                                   value,          /* Initial value                */
  294.                                   INT_MAX,        /* Maximum value                */
  295.                                   NULL);          /* Name of semaphore            */
  296.  
  297.     dprintf(("%ld: PyThread_allocate_sema() -> %p\n", PyThread_get_thread_ident(), aSemaphore));
  298.  
  299.     return (PyThread_type_sema) aSemaphore;
  300. }
  301.  
  302. void PyThread_free_sema(PyThread_type_sema aSemaphore)
  303. {
  304.     dprintf(("%ld: PyThread_free_sema(%p) called\n", PyThread_get_thread_ident(), aSemaphore));
  305.  
  306.     CloseHandle((HANDLE) aSemaphore);
  307. }
  308.  
  309. /*
  310.   XXX must do something about waitflag
  311.  */
  312. int PyThread_down_sema(PyThread_type_sema aSemaphore, int waitflag)
  313. {
  314.     DWORD waitResult;
  315.  
  316.     dprintf(("%ld: PyThread_down_sema(%p) called\n", PyThread_get_thread_ident(), aSemaphore));
  317.  
  318.     waitResult = WaitForSingleObject( (HANDLE) aSemaphore, INFINITE);
  319.  
  320.     dprintf(("%ld: PyThread_down_sema(%p) return: %l\n", PyThread_get_thread_ident(), aSemaphore, waitResult));
  321.     return 0;
  322. }
  323.  
  324. void PyThread_up_sema(PyThread_type_sema aSemaphore)
  325. {
  326.     ReleaseSemaphore(
  327.                 (HANDLE) aSemaphore,            /* Handle of semaphore                          */
  328.                 1,                              /* increment count by one                       */
  329.                 NULL);                          /* not interested in previous count             */
  330.                                                 
  331.     dprintf(("%ld: PyThread_up_sema(%p)\n", PyThread_get_thread_ident(), aSemaphore));
  332. }
  333.