/* thread.h: Locking and threading module definitions Copyright 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc. Written by Marco Fuykschot <marco@ddi.nl> Major update 2001 Robert Collins <rbtcollins@hotmail.com> This file is part of Cygwin. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #ifndef _CYGNUS_THREADS_ #define _CYGNUS_THREADS_ #define LOCK_FD_LIST 1 #define LOCK_MEMORY_LIST 2 #define LOCK_MMAP_LIST 3 #define LOCK_DLL_LIST 4 #define WRITE_LOCK 1 #define READ_LOCK 2 extern "C" { #if defined (_CYG_THREAD_FAILSAFE) && defined (_MT_SAFE) void AssertResourceOwner (int, int); #else #define AssertResourceOwner(i,ii) #endif } #ifndef _MT_SAFE #define SetResourceLock(i,n,c) #define ReleaseResourceLock(i,n,c) #else #include <pthread.h> #include <limits.h> #include <errno.h> #include <signal.h> #include <pwd.h> #include <grp.h> #include <security.h> #define _NOMNTENT_FUNCS #include <mntent.h> extern "C" { struct _winsup_t { /* Needed for the group functions */ struct __group16 _grp; char *_namearray[2]; int _grp_pos; /* console.cc */ unsigned _rarg; /* dlfcn.cc */ int _dl_error; char _dl_buffer[256]; /* passwd.cc */ struct passwd _res; char _pass[_PASSWORD_LEN]; int _pw_pos; /* path.cc */ struct mntent mntbuf; int _iteration; DWORD available_drives; char mnt_type[80]; char mnt_opts[80]; char mnt_fsname[CYG_MAX_PATH]; char mnt_dir[CYG_MAX_PATH]; /* strerror */ char _strerror_buf[20]; /* sysloc.cc */ char *_process_ident; int _process_logopt; int _process_facility; int _process_logmask; /* times.cc */ char timezone_buf[20]; struct tm _localtime_buf; /* uinfo.cc */ char _username[UNLEN + 1]; /* net.cc */ char *_ntoa_buf; struct protoent *_protoent_buf; struct servent *_servent_buf; struct hostent *_hostent_buf; }; struct __reent_t { struct _reent *_clib; struct _winsup_t *_winsup; void init_clib (_reent&); }; _winsup_t *_reent_winsup (); void SetResourceLock (int, int, const char *) __attribute__ ((regparm (3))); void ReleaseResourceLock (int, int, const char *) __attribute__ ((regparm (3))); #ifdef _CYG_THREAD_FAILSAFE void AssertResourceOwner (int, int); #else #define AssertResourceOwner(i,ii) #endif } class fast_mutex { public: fast_mutex () : lock_counter (0), win32_obj_id (0) { } ~fast_mutex () { if(win32_obj_id) CloseHandle (win32_obj_id); } bool init () { lock_counter = 0; win32_obj_id = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL); if (!win32_obj_id) { debug_printf ("CreateSemaphore failed. %E"); return false; } return true; } void lock () { if (InterlockedIncrement ((long *)&lock_counter) != 1) WaitForSingleObject (win32_obj_id, INFINITE); } void unlock () { if (InterlockedDecrement ((long *)&lock_counter)) ::ReleaseSemaphore (win32_obj_id, 1, NULL); } private: unsigned long lock_counter; HANDLE win32_obj_id; }; class per_process; class pinfo; class ResourceLocks { public: ResourceLocks () { } LPCRITICAL_SECTION Lock (int); void Init (); void Delete (); #ifdef _CYG_THREAD_FAILSAFE DWORD owner; DWORD count; #endif private: CRITICAL_SECTION lock; bool inited; }; #define PTHREAD_MAGIC 0xdf0df045 #define PTHREAD_MUTEX_MAGIC PTHREAD_MAGIC+1 #define PTHREAD_KEY_MAGIC PTHREAD_MAGIC+2 #define PTHREAD_ATTR_MAGIC PTHREAD_MAGIC+3 #define PTHREAD_MUTEXATTR_MAGIC PTHREAD_MAGIC+4 #define PTHREAD_COND_MAGIC PTHREAD_MAGIC+5 #define PTHREAD_CONDATTR_MAGIC PTHREAD_MAGIC+6 #define SEM_MAGIC PTHREAD_MAGIC+7 #define PTHREAD_ONCE_MAGIC PTHREAD_MAGIC+8 #define PTHREAD_RWLOCK_MAGIC PTHREAD_MAGIC+9 #define PTHREAD_RWLOCKATTR_MAGIC PTHREAD_MAGIC+10 #define MUTEX_OWNER_ANONYMOUS ((pthread_t) -1) /* verifyable_object should not be defined here - it's a general purpose class */ class verifyable_object { public: long magic; verifyable_object (long); virtual ~verifyable_object (); }; typedef enum { VALID_OBJECT, INVALID_OBJECT, VALID_STATIC_OBJECT } verifyable_object_state; verifyable_object_state verifyable_object_isvalid (void const *, long); verifyable_object_state verifyable_object_isvalid (void const *, long, void *); template <class list_node> inline void List_insert (fast_mutex &mx, list_node *&head, list_node *node) { if (!node) return; mx.lock (); node->next = head; head = node; mx.unlock (); } template <class list_node> inline void List_remove (fast_mutex &mx, list_node *&head, list_node *node) { if (!node) return; mx.lock (); if (node == head) head = head->next; else if (head) { list_node *cur = head; while (cur->next && node != cur->next) cur = cur->next; if (node == cur->next) cur->next = cur->next->next; } mx.unlock (); } template <class list_node> class List { public: List() : head(NULL) { mx_init (); } ~List() { } void fixup_after_fork () { mx_init (); } void insert (list_node *node) { List_insert (mx, head, node); } void remove (list_node *node) { List_remove (mx, head, node); } void for_each (void (list_node::*callback) ()) { mx.lock (); list_node *cur = head; while (cur) { (cur->*callback) (); cur = cur->next; } mx.unlock (); } protected: void mx_init () { if (!mx.init ()) api_fatal ("Could not create mutex for list synchronisation."); } fast_mutex mx; list_node *head; }; class pthread_key:public verifyable_object { public: static bool is_good_object (pthread_key_t const *); DWORD tls_index; int set (const void *); void *get () const; pthread_key (void (*)(void *)); ~pthread_key (); static void fixup_before_fork () { keys.for_each (&pthread_key::_fixup_before_fork); } static void fixup_after_fork () { keys.fixup_after_fork (); keys.for_each (&pthread_key::_fixup_after_fork); } static void run_all_destructors () { keys.for_each (&pthread_key::run_destructor); } /* List support calls */ class pthread_key *next; private: static List<pthread_key> keys; void _fixup_before_fork (); void _fixup_after_fork (); void (*destructor) (void *); void run_destructor (); void *fork_buf; }; class pthread_attr:public verifyable_object { public: static bool is_good_object(pthread_attr_t const *); int joinable; int contentionscope; int inheritsched; struct sched_param schedparam; size_t stacksize; pthread_attr (); ~pthread_attr (); }; class pthread_mutexattr:public verifyable_object { public: static bool is_good_object(pthread_mutexattr_t const *); int pshared; int mutextype; pthread_mutexattr (); ~pthread_mutexattr (); }; class pthread_mutex:public verifyable_object { public: static bool is_good_object (pthread_mutex_t const *); static bool is_good_initializer (pthread_mutex_t const *); static bool is_good_initializer_or_object (pthread_mutex_t const *); static bool is_good_initializer_or_bad_object (pthread_mutex_t const *mutex); static bool can_be_unlocked (pthread_mutex_t const *mutex); static void init_mutex (); static int init (pthread_mutex_t *, const pthread_mutexattr_t *); unsigned long lock_counter; HANDLE win32_obj_id; unsigned int recursion_counter; LONG condwaits; pthread_t owner; int type; int pshared; pthread_t get_pthread_self () const { return PTHREAD_MUTEX_NORMAL == type ? MUTEX_OWNER_ANONYMOUS : ::pthread_self (); } int lock () { return _lock (get_pthread_self ()); } int trylock () { return _trylock (get_pthread_self ()); } int unlock () { return _unlock (get_pthread_self ()); } int destroy () { return _destroy (get_pthread_self ()); } void set_owner (pthread_t self) { recursion_counter = 1; owner = self; } int lock_recursive () { if (UINT_MAX == recursion_counter) return EAGAIN; ++recursion_counter; return 0; } pthread_mutex (pthread_mutexattr * = NULL); pthread_mutex (pthread_mutex_t *, pthread_mutexattr *); ~pthread_mutex (); class pthread_mutex * next; static void fixup_after_fork () { mutexes.fixup_after_fork (); mutexes.for_each (&pthread_mutex::_fixup_after_fork); } private: int _lock (pthread_t self); int _trylock (pthread_t self); int _unlock (pthread_t self); int _destroy (pthread_t self); void _fixup_after_fork (); static List<pthread_mutex> mutexes; static fast_mutex mutex_initialization_lock; }; #define WAIT_CANCELED (WAIT_OBJECT_0 + 1) class pthread:public verifyable_object { public: HANDLE win32_obj_id; class pthread_attr attr; void *(*function) (void *); void *arg; void *return_ptr; bool valid; bool suspended; int cancelstate, canceltype; HANDLE cancel_event; pthread_t joiner; /* signal handling */ struct sigaction *sigs; sigset_t *sigmask; LONG *sigtodo; virtual void create (void *(*)(void *), pthread_attr *, void *); pthread (); virtual ~pthread (); static void init_mainthread (); static bool is_good_object(pthread_t const *); static void atforkprepare(); static void atforkparent(); static void atforkchild(); /* API calls */ static int cancel (pthread_t); static int join (pthread_t * thread, void **return_val); static int detach (pthread_t * thread); static int create (pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine) (void *), void *arg); static int once (pthread_once_t *, void (*)(void)); static int atfork(void (*)(void), void (*)(void), void (*)(void)); static int suspend (pthread_t * thread); static int resume (pthread_t * thread); virtual void exit (void *value_ptr); virtual int cancel (); virtual void testcancel (); static void static_cancel_self (); static DWORD cancelable_wait (HANDLE object, DWORD timeout, const bool do_cancel = true); virtual int setcancelstate (int state, int *oldstate); virtual int setcanceltype (int type, int *oldtype); virtual void push_cleanup_handler (__pthread_cleanup_handler *handler); virtual void pop_cleanup_handler (int const execute); static pthread* self (); static void *thread_init_wrapper (void *); virtual unsigned long getsequence_np(); static int equal (pthread_t t1, pthread_t t2) { return t1 == t2; } /* List support calls */ class pthread *next; static void fixup_after_fork () { threads.fixup_after_fork (); threads.for_each (&pthread::_fixup_after_fork); } static void suspend_all_except_self () { threads.for_each (&pthread::suspend_except_self); } static void resume_all () { threads.for_each (&pthread::resume); } private: static List<pthread> threads; DWORD thread_id; __pthread_cleanup_handler *cleanup_stack; pthread_mutex mutex; void suspend_except_self (); void resume (); void _fixup_after_fork (); void pop_all_cleanup_handlers (void); void precreate (pthread_attr *); void postcreate (); void set_thread_id_to_current (); static void set_tls_self_pointer (pthread *); static pthread *get_tls_self_pointer (); void cancel_self (); DWORD get_thread_id (); void init_current_thread (); }; class pthread_null : public pthread { public: static pthread *get_null_pthread(); ~pthread_null(); /* From pthread These should never get called * as the ojbect is not verifyable */ void create (void *(*)(void *), pthread_attr *, void *); void exit (void *value_ptr); int cancel (); void testcancel (); int setcancelstate (int state, int *oldstate); int setcanceltype (int type, int *oldtype); void push_cleanup_handler (__pthread_cleanup_handler *handler); void pop_cleanup_handler (int const execute); unsigned long getsequence_np(); private: pthread_null (); static pthread_null _instance; }; class pthread_condattr:public verifyable_object { public: static bool is_good_object(pthread_condattr_t const *); int shared; pthread_condattr (); ~pthread_condattr (); }; class pthread_cond:public verifyable_object { public: static bool is_good_object (pthread_cond_t const *); static bool is_good_initializer (pthread_cond_t const *); static bool is_good_initializer_or_object (pthread_cond_t const *); static bool is_good_initializer_or_bad_object (pthread_cond_t const *); static void init_mutex (); static int init (pthread_cond_t *, const pthread_condattr_t *); int shared; unsigned long waiting; unsigned long pending; HANDLE sem_wait; pthread_mutex mtx_in; pthread_mutex mtx_out; pthread_mutex_t mtx_cond; void unblock (const bool all); int wait (pthread_mutex_t mutex, DWORD dwMilliseconds = INFINITE); pthread_cond (pthread_condattr *); ~pthread_cond (); class pthread_cond * next; static void fixup_after_fork () { conds.fixup_after_fork (); conds.for_each (&pthread_cond::_fixup_after_fork); } private: void _fixup_after_fork (); static List<pthread_cond> conds; static fast_mutex cond_initialization_lock; }; class pthread_rwlockattr:public verifyable_object { public: static bool is_good_object(pthread_rwlockattr_t const *); int shared; pthread_rwlockattr (); ~pthread_rwlockattr (); }; class pthread_rwlock:public verifyable_object { public: static bool is_good_object (pthread_rwlock_t const *); static bool is_good_initializer (pthread_rwlock_t const *); static bool is_good_initializer_or_object (pthread_rwlock_t const *); static bool is_good_initializer_or_bad_object (pthread_rwlock_t const *); static void init_mutex (); static int init (pthread_rwlock_t *, const pthread_rwlockattr_t *); int shared; unsigned long waiting_readers; unsigned long waiting_writers; pthread_t writer; struct RWLOCK_READER { struct RWLOCK_READER *next; pthread_t thread; } *readers; fast_mutex readers_mx; int rdlock (); int tryrdlock (); int wrlock (); int trywrlock (); int unlock (); pthread_mutex mtx; pthread_cond cond_readers; pthread_cond cond_writers; pthread_rwlock (pthread_rwlockattr *); ~pthread_rwlock (); class pthread_rwlock * next; static void fixup_after_fork () { rwlocks.fixup_after_fork (); rwlocks.for_each (&pthread_rwlock::_fixup_after_fork); } private: static List<pthread_rwlock> rwlocks; void add_reader (struct RWLOCK_READER *rd); void remove_reader (struct RWLOCK_READER *rd); struct RWLOCK_READER *lookup_reader (pthread_t thread); void release () { if (waiting_writers) { if (!readers) cond_writers.unblock (false); } else if (waiting_readers) cond_readers.unblock (true); } static void rdlock_cleanup (void *arg); static void wrlock_cleanup (void *arg); void _fixup_after_fork (); static fast_mutex rwlock_initialization_lock; }; class pthread_once { public: pthread_mutex_t mutex; int state; }; /* shouldn't be here */ class semaphore:public verifyable_object { public: static bool is_good_object(sem_t const *); /* API calls */ static int init (sem_t * sem, int pshared, unsigned int value); static int destroy (sem_t * sem); static sem_t *open (const char *name, int oflag, mode_t mode, unsigned int value); static int wait (sem_t * sem); static int post (sem_t * sem); static int getvalue (sem_t * sem, int *sval); static int trywait (sem_t * sem); static int timedwait (sem_t * sem, const struct timespec *abstime); HANDLE win32_obj_id; int shared; long currentvalue; char *name; semaphore (int, unsigned int); semaphore (const char *name, int oflag, mode_t mode, unsigned int value); ~semaphore (); class semaphore * next; static void fixup_after_fork () { semaphores.fixup_after_fork (); semaphores.for_each (&semaphore::_fixup_after_fork); } private: void _wait (); void _post (); int _getvalue (int *sval); int _trywait (); int _timedwait (const struct timespec *abstime); void _fixup_after_fork (); static List<semaphore> semaphores; }; class callback { public: void (*cb)(void); class callback * next; }; class MTinterface { public: // General int concurrency; long int threadcount; // Used for main thread data, and sigproc thread struct __reent_t reents; struct _winsup_t winsup_reent; callback *pthread_prepare; callback *pthread_child; callback *pthread_parent; pthread_key reent_key; pthread_key thread_self_key; void Init (); void fixup_before_fork (void); void fixup_after_fork (void); MTinterface () : concurrency (0), threadcount (1), pthread_prepare (NULL), pthread_child (NULL), pthread_parent (NULL), reent_key (NULL), thread_self_key (NULL) { } }; #define MT_INTERFACE user_data->threadinterface #endif // MT_SAFE #endif // _CYGNUS_THREADS_