1 /*
2  * implement.h
3  *
4  * Definitions that don't need to be public.
5  *
6  * Keeps all the internals out of pthread.h
7  *
8  * --------------------------------------------------------------------------
9  *
10  *      Pthreads-win32 - POSIX Threads Library for Win32
11  *      Copyright(C) 1998 John E. Bossom
12  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
13  *
14  *      Contact Email: rpj@callisto.canberra.edu.au
15  *
16  *      The current list of contributors is contained
17  *      in the file CONTRIBUTORS included with the source
18  *      code distribution. The list can also be seen at the
19  *      following World Wide Web location:
20  *      http://sources.redhat.com/pthreads-win32/contributors.html
21  *
22  *      This library is free software; you can redistribute it and/or
23  *      modify it under the terms of the GNU Lesser General Public
24  *      License as published by the Free Software Foundation; either
25  *      version 2 of the License, or (at your option) any later version.
26  *
27  *      This library is distributed in the hope that it will be useful,
28  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
29  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
30  *      Lesser General Public License for more details.
31  *
32  *      You should have received a copy of the GNU Lesser General Public
33  *      License along with this library in the file COPYING.LIB;
34  *      if not, write to the Free Software Foundation, Inc.,
35  *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
36  */
37 
38 #ifndef _IMPLEMENT_H
39 #define _IMPLEMENT_H
40 
41 #ifdef _WIN32_WINNT
42 #undef _WIN32_WINNT
43 #endif
44 #define _WIN32_WINNT 0x400
45 
46 #include <windows.h>
47 
48 /*
49  * In case windows.h doesn't define it (e.g. WinCE perhaps)
50  */
51 #ifdef WINCE
52 typedef VOID (APIENTRY *PAPCFUNC)(DWORD dwParam);
53 #endif
54 
55 /*
56  * note: ETIMEDOUT is correctly defined in winsock.h
57  */
58 #include <winsock.h>
59 
60 /*
61  * In case ETIMEDOUT hasn't been defined above somehow.
62  */
63 #ifndef ETIMEDOUT
64 #  define ETIMEDOUT 10060	/* This is the value in winsock.h. */
65 #endif
66 
67 #if !defined(malloc)
68 #include <malloc.h>
69 #endif
70 
71 #if !defined(INT_MAX)
72 #include <limits.h>
73 #endif
74 
75 /* use local include files during development */
76 #include "semaphore.h"
77 #include "sched.h"
78 
79 #if defined(HAVE_C_INLINE) || defined(__cplusplus)
80 #define INLINE inline
81 #else
82 #define INLINE
83 #endif
84 
85 #if defined (__MINGW32__) || (_MSC_VER >= 1300)
86 #define PTW32_INTERLOCKED_LONG long
87 #define PTW32_INTERLOCKED_LPLONG long*
88 #else
89 #define PTW32_INTERLOCKED_LONG PVOID
90 #define PTW32_INTERLOCKED_LPLONG PVOID*
91 #endif
92 
93 #if defined(__MINGW32__)
94 #include <stdint.h>
95 #elif defined(__BORLANDC__)
96 #define int64_t ULONGLONG
97 #else
98 #define int64_t _int64
99 #endif
100 
101 typedef enum
102 {
103   /*
104    * This enumeration represents the state of the thread;
105    * The thread is still "alive" if the numeric value of the
106    * state is greater or equal "PThreadStateRunning".
107    */
108   PThreadStateInitial = 0,	/* Thread not running                   */
109   PThreadStateRunning,		/* Thread alive & kicking               */
110   PThreadStateSuspended,	/* Thread alive but suspended           */
111   PThreadStateCancelPending,	/* Thread alive but is                  */
112   /* has cancelation pending.        */
113   PThreadStateCanceling,	/* Thread alive but is                  */
114   /* in the process of terminating        */
115   /* due to a cancellation request        */
116   PThreadStateException,	/* Thread alive but exiting             */
117   /* due to an exception                  */
118   PThreadStateLast
119 }
120 PThreadState;
121 
122 
123 typedef struct ptw32_thread_t_ ptw32_thread_t;
124 
125 struct ptw32_thread_t_
126 {
127 #ifdef _UWIN
128   DWORD dummy[5];
129 #endif
130   DWORD thread;
131   HANDLE threadH;		/* Win32 thread handle - POSIX thread is invalid if threadH == 0 */
132   pthread_t ptHandle;		/* This thread's permanent pthread_t handle */
133   ptw32_thread_t * prevReuse;	/* Links threads on reuse stack */
134   volatile PThreadState state;
135   void *exitStatus;
136   void *parms;
137   int ptErrno;
138   int detachState;
139   pthread_mutex_t threadLock;	/* Used for serialised access to public thread state */
140   int sched_priority;		/* As set, not as currently is */
141   pthread_mutex_t cancelLock;	/* Used for async-cancel safety */
142   int cancelState;
143   int cancelType;
144   HANDLE cancelEvent;
145 #ifdef __CLEANUP_C
146   jmp_buf start_mark;
147 #endif				/* __CLEANUP_C */
148 #if HAVE_SIGSET_T
149   sigset_t sigmask;
150 #endif				/* HAVE_SIGSET_T */
151   int implicit:1;
152   void *keys;
153   void *nextAssoc;
154 };
155 
156 
157 /*
158  * Special value to mark attribute objects as valid.
159  */
160 #define PTW32_ATTR_VALID ((unsigned long) 0xC4C0FFEE)
161 
162 struct pthread_attr_t_
163 {
164   unsigned long valid;
165   void *stackaddr;
166   size_t stacksize;
167   int detachstate;
168   struct sched_param param;
169   int inheritsched;
170   int contentionscope;
171 #if HAVE_SIGSET_T
172   sigset_t sigmask;
173 #endif				/* HAVE_SIGSET_T */
174 };
175 
176 
177 /*
178  * ====================
179  * ====================
180  * Semaphores, Mutexes and Condition Variables
181  * ====================
182  * ====================
183  */
184 
185 struct sem_t_
186 {
187   int value;
188   pthread_mutex_t lock;
189   HANDLE sem;
190 #ifdef NEED_SEM
191   int leftToUnblock;
192 #endif
193 };
194 
195 #define PTW32_OBJECT_AUTO_INIT ((void *) -1)
196 #define PTW32_OBJECT_INVALID   NULL
197 
198 struct pthread_mutex_t_
199 {
200   LONG lock_idx;		/* Provides exclusive access to mutex state
201 				   via the Interlocked* mechanism.
202 				    0: unlocked/free.
203 				    1: locked - no other waiters.
204 				   -1: locked - with possible other waiters.
205 				*/
206   int recursive_count;		/* Number of unlocks a thread needs to perform
207 				   before the lock is released (recursive
208 				   mutexes only). */
209   int kind;			/* Mutex type. */
210   pthread_t ownerThread;
211   HANDLE event;			/* Mutex release notification to waiting
212 				   threads. */
213 };
214 
215 struct pthread_mutexattr_t_
216 {
217   int pshared;
218   int kind;
219 };
220 
221 /*
222  * Possible values, other than PTW32_OBJECT_INVALID,
223  * for the "interlock" element in a spinlock.
224  *
225  * In this implementation, when a spinlock is initialised,
226  * the number of cpus available to the process is checked.
227  * If there is only one cpu then "interlock" is set equal to
228  * PTW32_SPIN_USE_MUTEX and u.mutex is a initialised mutex.
229  * If the number of cpus is greater than 1 then "interlock"
230  * is set equal to PTW32_SPIN_UNLOCKED and the number is
231  * stored in u.cpus. This arrangement allows the spinlock
232  * routines to attempt an InterlockedCompareExchange on "interlock"
233  * immediately and, if that fails, to try the inferior mutex.
234  *
235  * "u.cpus" isn't used for anything yet, but could be used at
236  * some point to optimise spinlock behaviour.
237  */
238 #define PTW32_SPIN_UNLOCKED    (1)
239 #define PTW32_SPIN_LOCKED      (2)
240 #define PTW32_SPIN_USE_MUTEX   (3)
241 
242 struct pthread_spinlock_t_
243 {
244   long interlock;		/* Locking element for multi-cpus. */
245   union
246   {
247     int cpus;			/* No. of cpus if multi cpus, or   */
248     pthread_mutex_t mutex;	/* mutex if single cpu.            */
249   } u;
250 };
251 
252 struct pthread_barrier_t_
253 {
254   unsigned int nCurrentBarrierHeight;
255   unsigned int nInitialBarrierHeight;
256   int iStep;
257   int pshared;
258   sem_t semBarrierBreeched[2];
259 };
260 
261 struct pthread_barrierattr_t_
262 {
263   int pshared;
264 };
265 
266 struct pthread_key_t_
267 {
268   DWORD key;
269   void (*destructor) (void *);
270   pthread_mutex_t keyLock;
271   void *threads;
272 };
273 
274 
275 typedef struct ThreadParms ThreadParms;
276 typedef struct ThreadKeyAssoc ThreadKeyAssoc;
277 
278 struct ThreadParms
279 {
280   pthread_t tid;
281   void *(*start) (void *);
282   void *arg;
283 };
284 
285 
286 struct pthread_cond_t_
287 {
288   long nWaitersBlocked;		/* Number of threads blocked            */
289   long nWaitersGone;		/* Number of threads timed out          */
290   long nWaitersToUnblock;	/* Number of threads to unblock         */
291   sem_t semBlockQueue;		/* Queue up threads waiting for the     */
292   /*   condition to become signalled      */
293   sem_t semBlockLock;		/* Semaphore that guards access to      */
294   /* | waiters blocked count/block queue  */
295   /* +-> Mandatory Sync.LEVEL-1           */
296   pthread_mutex_t mtxUnblockLock;	/* Mutex that guards access to          */
297   /* | waiters (to)unblock(ed) counts     */
298   /* +-> Optional* Sync.LEVEL-2           */
299   pthread_cond_t next;		/* Doubly linked list                   */
300   pthread_cond_t prev;
301 };
302 
303 
304 struct pthread_condattr_t_
305 {
306   int pshared;
307 };
308 
309 #define PTW32_RWLOCK_MAGIC 0xfacade2
310 
311 struct pthread_rwlock_t_
312 {
313   pthread_mutex_t mtxExclusiveAccess;
314   pthread_mutex_t mtxSharedAccessCompleted;
315   pthread_cond_t cndSharedAccessCompleted;
316   int nSharedAccessCount;
317   int nExclusiveAccessCount;
318   int nCompletedSharedAccessCount;
319   int nMagic;
320 };
321 
322 struct pthread_rwlockattr_t_
323 {
324   int pshared;
325 };
326 
327 /*
328  * MCS lock queue node - see ptw32_MCS_lock.c
329  */
330 struct ptw32_mcs_node_t_
331 {
332   struct ptw32_mcs_node_t_ **lock;        /* ptr to tail of queue */
333   struct ptw32_mcs_node_t_  *next;        /* ptr to successor in queue */
334   LONG                       readyFlag;   /* set after lock is released by
335                                              predecessor */
336   LONG                       nextFlag;    /* set after 'next' ptr is set by
337                                              successor */
338 };
339 
340 typedef struct ptw32_mcs_node_t_   ptw32_mcs_local_node_t;
341 typedef struct ptw32_mcs_node_t_  *ptw32_mcs_lock_t;
342 
343 
344 struct ThreadKeyAssoc
345 {
346   /*
347    * Purpose:
348    *      This structure creates an association between a thread and a key.
349    *      It is used to implement the implicit invocation of a user defined
350    *      destroy routine for thread specific data registered by a user upon
351    *      exiting a thread.
352    *
353    *      Graphically, the arrangement is as follows, where:
354    *
355    *         K - Key with destructor
356    *            (head of chain is key->threads)
357    *         T - Thread that has called pthread_setspecific(Kn)
358    *            (head of chain is thread->keys)
359    *         A - Association. Each association is a node at the
360    *             intersection of two doubly-linked lists.
361    *
362    *                 T1    T2    T3
363    *                 |     |     |
364    *                 |     |     |
365    *         K1 -----+-----A-----A----->
366    *                 |     |     |
367    *                 |     |     |
368    *         K2 -----A-----A-----+----->
369    *                 |     |     |
370    *                 |     |     |
371    *         K3 -----A-----+-----A----->
372    *                 |     |     |
373    *                 |     |     |
374    *                 V     V     V
375    *
376    *      Access to the association is guarded by two locks: the key's
377    *      general lock (guarding the row) and the thread's general
378    *      lock (guarding the column). This avoids the need for a
379    *      dedicated lock for each association, which not only consumes
380    *      more handles but requires that: before the lock handle can
381    *      be released - both the key must be deleted and the thread
382    *      must have called the destructor. The two-lock arrangement
383    *      allows the resources to be freed as soon as either thread or
384    *      key is concluded.
385    *
386    *      To avoid deadlock: whenever both locks are required, the key
387    *      and thread locks are always acquired in the order: key lock
388    *      then thread lock. An exception to this exists when a thread
389    *      calls the destructors, however this is done carefully to
390    *      avoid deadlock.
391    *
392    *      An association is created when a thread first calls
393    *      pthread_setspecific() on a key that has a specified
394    *      destructor.
395    *
396    *      An association is destroyed either immediately after the
397    *      thread calls the key destructor function on thread exit, or
398    *      when the key is deleted.
399    *
400    * Attributes:
401    *      thread
402    *              reference to the thread that owns the
403    *              association. This is actually the pointer to the
404    *              thread struct itself. Since the association is
405    *              destroyed before the thread exits, this can never
406    *              point to a different logical thread to the one that
407    *              created the assoc, i.e. after thread struct reuse.
408    *
409    *      key
410    *              reference to the key that owns the association.
411    *
412    *      nextKey
413    *              The pthread_t->keys attribute is the head of a
414    *              chain of associations that runs through the nextKey
415    *              link. This chain provides the 1 to many relationship
416    *              between a pthread_t and all pthread_key_t on which
417    *              it called pthread_setspecific.
418    *
419    *      prevKey
420    *              Similarly.
421    *
422    *      nextThread
423    *              The pthread_key_t->threads attribute is the head of
424    *              a chain of assoctiations that runs through the
425    *              nextThreads link. This chain provides the 1 to many
426    *              relationship between a pthread_key_t and all the
427    *              PThreads that have called pthread_setspecific for
428    *              this pthread_key_t.
429    *
430    *      prevThread
431    *              Similarly.
432    *
433    * Notes:
434    *      1)      As soon as either the key or the thread is no longer
435    *              referencing the association, it can be destroyed. The
436    *              association will be removed from both chains.
437    *
438    *      2)      Under WIN32, an association is only created by
439    *              pthread_setspecific if the user provided a
440    *              destroyRoutine when they created the key.
441    *
442    *
443    */
444   ptw32_thread_t * thread;
445   pthread_key_t key;
446   ThreadKeyAssoc *nextKey;
447   ThreadKeyAssoc *nextThread;
448   ThreadKeyAssoc *prevKey;
449   ThreadKeyAssoc *prevThread;
450 };
451 
452 
453 #ifdef __CLEANUP_SEH
454 /*
455  * --------------------------------------------------------------
456  * MAKE_SOFTWARE_EXCEPTION
457  *      This macro constructs a software exception code following
458  *      the same format as the standard Win32 error codes as defined
459  *      in WINERROR.H
460  *  Values are 32 bit values layed out as follows:
461  *
462  *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
463  *  +---+-+-+-----------------------+-------------------------------+
464  *  |Sev|C|R|     Facility          |               Code            |
465  *  +---+-+-+-----------------------+-------------------------------+
466  *
467  * Severity Values:
468  */
469 #define SE_SUCCESS              0x00
470 #define SE_INFORMATION          0x01
471 #define SE_WARNING              0x02
472 #define SE_ERROR                0x03
473 
474 #define MAKE_SOFTWARE_EXCEPTION( _severity, _facility, _exception ) \
475 ( (DWORD) ( ( (_severity) << 30 ) |     /* Severity code        */ \
476             ( 1 << 29 ) |               /* MS=0, User=1         */ \
477             ( 0 << 28 ) |               /* Reserved             */ \
478             ( (_facility) << 16 ) |     /* Facility Code        */ \
479             ( (_exception) <<  0 )      /* Exception Code       */ \
480             ) )
481 
482 /*
483  * We choose one specific Facility/Error code combination to
484  * identify our software exceptions vs. WIN32 exceptions.
485  * We store our actual component and error code within
486  * the optional information array.
487  */
488 #define EXCEPTION_PTW32_SERVICES        \
489      MAKE_SOFTWARE_EXCEPTION( SE_ERROR, \
490                               PTW32_SERVICES_FACILITY, \
491                               PTW32_SERVICES_ERROR )
492 
493 #define PTW32_SERVICES_FACILITY         0xBAD
494 #define PTW32_SERVICES_ERROR            0xDEED
495 
496 #endif /* __CLEANUP_SEH */
497 
498 /*
499  * Services available through EXCEPTION_PTW32_SERVICES
500  * and also used [as parameters to ptw32_throw()] as
501  * generic exception selectors.
502  */
503 
504 #define PTW32_EPS_EXIT                  (1)
505 #define PTW32_EPS_CANCEL                (2)
506 
507 
508 /* Useful macros */
509 #define PTW32_MAX(a,b)  ((a)<(b)?(b):(a))
510 #define PTW32_MIN(a,b)  ((a)>(b)?(b):(a))
511 
512 
513 /* Declared in global.c */
514 extern PTW32_INTERLOCKED_LONG (WINAPI *
515 			       ptw32_interlocked_compare_exchange)
516   (PTW32_INTERLOCKED_LPLONG, PTW32_INTERLOCKED_LONG, PTW32_INTERLOCKED_LONG);
517 
518 /* Declared in pthread_cancel.c */
519 extern DWORD (*ptw32_register_cancelation) (PAPCFUNC, HANDLE, DWORD);
520 
521 /* Thread Reuse stack bottom marker. Must not be NULL or any valid pointer to memory. */
522 #define PTW32_THREAD_REUSE_EMPTY ((ptw32_thread_t *) 1)
523 
524 extern int ptw32_processInitialized;
525 extern ptw32_thread_t * ptw32_threadReuseTop;
526 extern ptw32_thread_t * ptw32_threadReuseBottom;
527 extern pthread_key_t ptw32_selfThreadKey;
528 extern pthread_key_t ptw32_cleanupKey;
529 extern pthread_cond_t ptw32_cond_list_head;
530 extern pthread_cond_t ptw32_cond_list_tail;
531 
532 extern int ptw32_mutex_default_kind;
533 
534 extern int ptw32_concurrency;
535 
536 extern int ptw32_features;
537 
538 extern BOOL ptw32_smp_system;  /* True: SMP system, False: Uni-processor system */
539 
540 extern CRITICAL_SECTION ptw32_thread_reuse_lock;
541 extern CRITICAL_SECTION ptw32_mutex_test_init_lock;
542 extern CRITICAL_SECTION ptw32_cond_list_lock;
543 extern CRITICAL_SECTION ptw32_cond_test_init_lock;
544 extern CRITICAL_SECTION ptw32_rwlock_test_init_lock;
545 extern CRITICAL_SECTION ptw32_spinlock_test_init_lock;
546 
547 #ifdef _UWIN
548 extern int pthread_count;
549 #endif
550 
551 #ifdef __cplusplus
552 extern "C"
553 {
554 #endif				/* __cplusplus */
555 
556 /*
557  * =====================
558  * =====================
559  * Forward Declarations
560  * =====================
561  * =====================
562  */
563 
564   int ptw32_is_attr (const pthread_attr_t * attr);
565 
566   int ptw32_cond_check_need_init (pthread_cond_t * cond);
567   int ptw32_mutex_check_need_init (pthread_mutex_t * mutex);
568   int ptw32_rwlock_check_need_init (pthread_rwlock_t * rwlock);
569 
570   PTW32_INTERLOCKED_LONG WINAPI
571     ptw32_InterlockedCompareExchange (PTW32_INTERLOCKED_LPLONG location,
572 				      PTW32_INTERLOCKED_LONG value,
573 				      PTW32_INTERLOCKED_LONG comparand);
574 
575   LONG WINAPI
576     ptw32_InterlockedExchange (LPLONG location,
577 			       LONG value);
578 
579   DWORD
580     ptw32_RegisterCancelation (PAPCFUNC callback,
581 			       HANDLE threadH, DWORD callback_arg);
582 
583   int ptw32_processInitialize (void);
584 
585   void ptw32_processTerminate (void);
586 
587   void ptw32_threadDestroy (pthread_t tid);
588 
589   void ptw32_pop_cleanup_all (int execute);
590 
591   pthread_t ptw32_new (void);
592 
593   pthread_t ptw32_threadReusePop (void);
594 
595   void ptw32_threadReusePush (pthread_t thread);
596 
597   int ptw32_getprocessors (int *count);
598 
599   int ptw32_setthreadpriority (pthread_t thread, int policy, int priority);
600 
601   void ptw32_rwlock_cancelwrwait (void *arg);
602 
603 #if ! defined (__MINGW32__) || defined (__MSVCRT__)
604   unsigned __stdcall
605 #else
606   void
607 #endif
608     ptw32_threadStart (void *vthreadParms);
609 
610   void ptw32_callUserDestroyRoutines (pthread_t thread);
611 
612   int ptw32_tkAssocCreate (ptw32_thread_t * thread, pthread_key_t key);
613 
614   void ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc);
615 
616   int ptw32_semwait (sem_t * sem);
617 
618   DWORD ptw32_relmillisecs (const struct timespec * abstime);
619 
620   void ptw32_mcs_lock_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node);
621 
622   void ptw32_mcs_lock_release (ptw32_mcs_local_node_t * node);
623 
624 #ifdef NEED_FTIME
625   void ptw32_timespec_to_filetime (const struct timespec *ts, FILETIME * ft);
626   void ptw32_filetime_to_timespec (const FILETIME * ft, struct timespec *ts);
627 #endif
628 
629 /* Declared in misc.c */
630 #ifdef NEED_CALLOC
631 #define calloc(n, s) ptw32_calloc(n, s)
632   void *ptw32_calloc (size_t n, size_t s);
633 #endif
634 
635 /* Declared in private.c */
636   void ptw32_throw (DWORD exception);
637 
638 #ifdef __cplusplus
639 }
640 #endif				/* __cplusplus */
641 
642 
643 #ifdef _UWIN_
644 #   ifdef       _MT
645 #       ifdef __cplusplus
646 extern "C"
647 {
648 #       endif
649   _CRTIMP unsigned long __cdecl _beginthread (void (__cdecl *) (void *),
650 					      unsigned, void *);
651   _CRTIMP void __cdecl _endthread (void);
652   _CRTIMP unsigned long __cdecl _beginthreadex (void *, unsigned,
653 						unsigned (__stdcall *) (void *),
654 						void *, unsigned, unsigned *);
655   _CRTIMP void __cdecl _endthreadex (unsigned);
656 #       ifdef __cplusplus
657 }
658 #       endif
659 #   endif
660 #else
661 #   include <process.h>
662 #endif
663 
664 
665 /*
666  * Defaults. Could be overridden when building the inlined version of the dll.
667  * See ptw32_InterlockedCompareExchange.c
668  */
669 #ifndef PTW32_INTERLOCKED_COMPARE_EXCHANGE
670 #define PTW32_INTERLOCKED_COMPARE_EXCHANGE ptw32_interlocked_compare_exchange
671 #endif
672 
673 #ifndef PTW32_INTERLOCKED_EXCHANGE
674 #define PTW32_INTERLOCKED_EXCHANGE InterlockedExchange
675 #endif
676 
677 
678 /*
679  * Check for old and new versions of cygwin. See the FAQ file:
680  *
681  * Question 1 - How do I get pthreads-win32 to link under Cygwin or Mingw32?
682  *
683  * Patch by Anders Norlander <anorland@hem2.passagen.se>
684  */
685 #if defined(__CYGWIN32__) || defined(__CYGWIN__) || defined(NEED_CREATETHREAD)
686 
687 /*
688  * Macro uses args so we can cast start_proc to LPTHREAD_START_ROUTINE
689  * in order to avoid warnings because of return type
690  */
691 
692 #define _beginthreadex(security, \
693                        stack_size, \
694                        start_proc, \
695                        arg, \
696                        flags, \
697                        pid) \
698         CreateThread(security, \
699                      stack_size, \
700                      (LPTHREAD_START_ROUTINE) start_proc, \
701                      arg, \
702                      flags, \
703                      pid)
704 
705 #define _endthreadex ExitThread
706 
707 #endif				/* __CYGWIN32__ || __CYGWIN__ || NEED_CREATETHREAD */
708 
709 
710 #endif				/* _IMPLEMENT_H */
711