diff options
author | Corinna Vinschen <vinschen@redhat.com> | 2007-11-29 11:14:50 +0000 |
---|---|---|
committer | Corinna Vinschen <vinschen@redhat.com> | 2007-11-29 11:14:50 +0000 |
commit | aaffc08d19b318f1d3923ec8d3a648d84948dc51 (patch) | |
tree | 364a4258f1ca9c92a8262069b43fc620f8d72c96 | |
parent | aaf339cf59104389ec9256c0226ab83c87c087c5 (diff) | |
download | gdb-aaffc08d19b318f1d3923ec8d3a648d84948dc51.tar.gz |
Drop old SetResourceLock stuff in favor of mutos.
* dcrt0.cc (_reslock): Remove.
(__cygwin_user_data): Accommodate removal of resourcelocks member.
(dll_crt0_0): Don't initialize resourcelocks.
* exceptions.cc (_cygtls::signal_exit): Drop resourcelocks handling.
* mmap.cc (mmap_guard): New muto.
(LIST_LOCK): Define.
(LIST_UNLOCK): Define.
(mmap_list::search_record): Remove.
(mmap_list::try_map): Include code for anonymous case from
mmap_list::search_record.
(mmap_is_attached_or_noreserve): Access bookkeeping lists in a thread
safe way.
(mmap64): Replace SetResourceLock/ReleaseResourceLock by
LIST_LOCK/LIST_UNLOCK. Lock at the latest possible point.
(munmap): Replace SetResourceLock/ReleaseResourceLock by
LIST_LOCK/LIST_UNLOCK.
(msync): Ditto.
(mprotect): Ditto.
* thread.cc (ResourceLocks::Lock): Remove.
(SetResourceLock): Remove.
(ReleaseResourceLock): Remove.
(ResourceLocks::Init): Remove.
(ResourceLocks::Delete): Remove.
* thread.h (SetResourceLock): Drop declaration.
(ReleaseResourceLock): Ditto.
(class ResourceLocks): Drop definition.
* include/sys/cygwin.h (class ResourceLocks): Drop forward declaration.
(struct per_process): Replace resourcelocks with additional unused2
element.
(per_process_overwrite): Accommodate above change.
* mmap.cc: Convert usage of dynamically growing cmalloced arrays to
cmalloced linked lists throughout.
(class mmap_record): Add LIST_ENTRY element.
(mmap_record::match): New method, taking over match algorithm from
list::search_record.
(class mmap_list): Rename from class list. Add LIST_ENTRY. Convert
recs to a LIST_HEAD. Drop nrecs and maxrecs members.
(mmap_list::get_record): Drop entirely.
(mmap_list::free_recs): Drop entirely.
(mmap_list::del_record): Take mmap_record to delete as parameter.
(mmap_list::search_record): Convert to mmap_record::match.
(class mmap_areas): Rename from class map. Convert lists to LIST_HEAD.
(mmap_areas::get_list): Drop entirely.
(mmap_areas::del_list): Take mmap_list to delete as parameter.
(mprotect): Fix indentation.
-rw-r--r-- | winsup/cygwin/ChangeLog | 52 | ||||
-rw-r--r-- | winsup/cygwin/dcrt0.cc | 6 | ||||
-rw-r--r-- | winsup/cygwin/exceptions.cc | 3 | ||||
-rw-r--r-- | winsup/cygwin/include/sys/cygwin.h | 271 | ||||
-rw-r--r-- | winsup/cygwin/mmap.cc | 417 | ||||
-rw-r--r-- | winsup/cygwin/thread.cc | 37 | ||||
-rw-r--r-- | winsup/cygwin/thread.h | 687 |
7 files changed, 1196 insertions, 277 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 338b5947d20..701433fab61 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,55 @@ +2007-11-27 Corinna Vinschen <corinna@vinschen.de> + + Drop old SetResourceLock stuff in favor of mutos. + * dcrt0.cc (_reslock): Remove. + (__cygwin_user_data): Accommodate removal of resourcelocks member. + (dll_crt0_0): Don't initialize resourcelocks. + * exceptions.cc (_cygtls::signal_exit): Drop resourcelocks handling. + * mmap.cc (mmap_guard): New muto. + (LIST_LOCK): Define. + (LIST_UNLOCK): Define. + (mmap_list::search_record): Remove. + (mmap_list::try_map): Include code for anonymous case from + mmap_list::search_record. + (mmap_is_attached_or_noreserve): Access bookkeeping lists in a thread + safe way. + (mmap64): Replace SetResourceLock/ReleaseResourceLock by + LIST_LOCK/LIST_UNLOCK. Lock at the latest possible point. + (munmap): Replace SetResourceLock/ReleaseResourceLock by + LIST_LOCK/LIST_UNLOCK. + (msync): Ditto. + (mprotect): Ditto. + * thread.cc (ResourceLocks::Lock): Remove. + (SetResourceLock): Remove. + (ReleaseResourceLock): Remove. + (ResourceLocks::Init): Remove. + (ResourceLocks::Delete): Remove. + * thread.h (SetResourceLock): Drop declaration. + (ReleaseResourceLock): Ditto. + (class ResourceLocks): Drop definition. + * include/sys/cygwin.h (class ResourceLocks): Drop forward declaration. + (struct per_process): Replace resourcelocks with additional unused2 + element. + (per_process_overwrite): Accommodate above change. + +2007-11-27 Corinna Vinschen <corinna@vinschen.de> + + * mmap.cc: Convert usage of dynamically growing cmalloced arrays to + cmalloced linked lists throughout. + (class mmap_record): Add LIST_ENTRY element. + (mmap_record::match): New method, taking over match algorithm from + list::search_record. + (class mmap_list): Rename from class list. Add LIST_ENTRY. Convert + recs to a LIST_HEAD. Drop nrecs and maxrecs members. + (mmap_list::get_record): Drop entirely. + (mmap_list::free_recs): Drop entirely. + (mmap_list::del_record): Take mmap_record to delete as parameter. + (mmap_list::search_record): Convert to mmap_record::match. + (class mmap_areas): Rename from class map. Convert lists to LIST_HEAD. + (mmap_areas::get_list): Drop entirely. + (mmap_areas::del_list): Take mmap_list to delete as parameter. + (mprotect): Fix indentation. + 2007-11-23 Christopher Faylor <me+cygwin@cgf.cx> * cygheap.cc (_crealloc): Avoid memcpy when _cmalloc returns NULL. diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 727163e0829..bd8b0e65017 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -81,7 +81,6 @@ bool NO_COPY cygwin_finished_initializing; measure to allow an orderly transfer to the new, correct sigmask method. */ unsigned NO_COPY int signal_shift_subtract = 1; -ResourceLocks _reslock NO_COPY; MTinterface _mtinterf; bool NO_COPY _cygwin_testing; @@ -114,8 +113,8 @@ extern "C" /* hmodule */ NULL, /* api_major */ CYGWIN_VERSION_API_MAJOR, /* api_minor */ CYGWIN_VERSION_API_MINOR, - /* unused2 */ {0, 0, 0, 0, 0}, - /* resourcelocks */ &_reslock, /* threadinterface */ &_mtinterf, + /* unused2 */ {0, 0, 0, 0, 0, 0}, + /* threadinterface */ &_mtinterf, /* impure_ptr */ _GLOBAL_REENT, }; bool ignore_case_with_glob; @@ -760,7 +759,6 @@ dll_crt0_0 () } } - user_data->resourcelocks->Init (); user_data->threadinterface->Init (); _cygtls::init (); diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index c05151d3273..2280f82b1a1 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -1294,9 +1294,6 @@ _cygtls::signal_exit (int rc) stupid. */ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL); - user_data->resourcelocks->Delete (); - user_data->resourcelocks->Init (); - sigproc_printf ("about to call do_exit (%x)", rc); SetEvent (signal_arrived); do_exit (rc); diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h new file mode 100644 index 00000000000..432811ca4d1 --- /dev/null +++ b/winsup/cygwin/include/sys/cygwin.h @@ -0,0 +1,271 @@ +/* sys/cygwin.h + + Copyright 1997, 1998, 2000, 2001, 2002 Red Hat, Inc. + +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 _SYS_CYGWIN_H +#define _SYS_CYGWIN_H + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define _CYGWIN_SIGNAL_STRING "cYgSiGw00f" + +extern pid_t cygwin32_winpid_to_pid (int); +extern void cygwin32_win32_to_posix_path_list (const char *, char *); +extern int cygwin32_win32_to_posix_path_list_buf_size (const char *); +extern void cygwin32_posix_to_win32_path_list (const char *, char *); +extern int cygwin32_posix_to_win32_path_list_buf_size (const char *); +extern int cygwin32_conv_to_win32_path (const char *, char *); +extern int cygwin32_conv_to_full_win32_path (const char *, char *); +extern void cygwin32_conv_to_posix_path (const char *, char *); +extern void cygwin32_conv_to_full_posix_path (const char *, char *); +extern int cygwin32_posix_path_list_p (const char *); +extern void cygwin32_split_path (const char *, char *, char *); + +extern pid_t cygwin_winpid_to_pid (int); +extern int cygwin_win32_to_posix_path_list (const char *, char *); +extern int cygwin_win32_to_posix_path_list_buf_size (const char *); +extern int cygwin_posix_to_win32_path_list (const char *, char *); +extern int cygwin_posix_to_win32_path_list_buf_size (const char *); +extern int cygwin_conv_to_win32_path (const char *, char *); +extern int cygwin_conv_to_full_win32_path (const char *, char *); +extern int cygwin_conv_to_posix_path (const char *, char *); +extern int cygwin_conv_to_full_posix_path (const char *, char *); +extern int cygwin_posix_path_list_p (const char *); +extern void cygwin_split_path (const char *, char *, char *); + +struct __cygwin_perfile +{ + const char *name; + unsigned flags; +}; + +/* External interface stuff */ + +/* Always add at the bottom. Do not add new values in the middle. */ +typedef enum + { + CW_LOCK_PINFO, + CW_UNLOCK_PINFO, + CW_GETTHREADNAME, + CW_GETPINFO, + CW_SETPINFO, + CW_SETTHREADNAME, + CW_GETVERSIONINFO, + CW_READ_V1_MOUNT_TABLES, + CW_USER_DATA, + CW_PERFILE, + CW_GET_CYGDRIVE_PREFIXES, + CW_GETPINFO_FULL, + CW_INIT_EXCEPTIONS, + CW_GET_CYGDRIVE_INFO, + CW_SET_CYGWIN_REGISTRY_NAME, + CW_GET_CYGWIN_REGISTRY_NAME, + CW_STRACE_TOGGLE, + CW_STRACE_ACTIVE, + CW_CYGWIN_PID_TO_WINPID, + CW_EXTRACT_DOMAIN_AND_USER, + CW_CMDLINE, + CW_CHECK_NTSEC, + CW_GET_ERRNO_FROM_WINERROR, + CW_GET_POSIX_SECURITY_ATTRIBUTE, + CW_GET_SHMLBA, + CW_GET_UID_FROM_SID, + CW_GET_GID_FROM_SID, + CW_GET_BINMODE, + CW_HOOK, + CW_ARGV, + CW_ENVP, + CW_DEBUG_SELF, + CW_SYNC_WINENV, + CW_CYGTLS_PADSIZE + } cygwin_getinfo_types; + +#define CW_NEXTPID 0x80000000 /* or with pid to get next one */ +unsigned long cygwin_internal (cygwin_getinfo_types, ...); + +/* Flags associated with process_state */ +enum +{ + PID_IN_USE = 0x00001, /* Entry in use. */ + PID_UNUSED = 0x00002, /* Available. */ + PID_STOPPED = 0x00004, /* Waiting for SIGCONT. */ + PID_TTYIN = 0x00008, /* Waiting for terminal input. */ + PID_TTYOU = 0x00010, /* Waiting for terminal output. */ + PID_ORPHANED = 0x00020, /* Member of an orphaned process group. */ + PID_ACTIVE = 0x00040, /* Pid accepts signals. */ + PID_CYGPARENT = 0x00080, /* Set if parent was a cygwin app. */ + PID_MAP_RW = 0x00100, /* Flag to open map rw. */ + PID_MYSELF = 0x00200, /* Flag that pid is me. */ + PID_NOCLDSTOP = 0x00400, /* Set if no SIGCHLD signal on stop. */ + PID_INITIALIZING = 0x00800, /* Set until ready to receive signals. */ + PID_USETTY = 0x01000, /* Setting this enables or disables cygwin's + tty support. This is inherited by + all execed or forked processes. */ + PID_ALLPIDS = 0x02000, /* used by pinfo scanner */ + PID_EXECED = 0x04000, /* redirect to original pid info block */ + PID_NOREDIR = 0x08000, /* don't redirect if execed */ + PID_EXITED = 0x80000000 /* Free entry. */ +}; + +#ifdef WINVER +#ifdef _PATH_PASSWD +extern HANDLE cygwin_logon_user (const struct passwd *, const char *); +#endif + +/* This lives in the app and is initialized before jumping into the DLL. + It should only contain stuff which the user's process needs to see, or + which is needed before the user pointer is initialized, or is needed to + carry inheritance information from parent to child. Note that it cannot + be used to carry inheritance information across exec! + + Remember, this structure is linked into the application's executable. + Changes to this can invalidate existing executables, so we go to extra + lengths to avoid having to do it. + + When adding/deleting members, remember to adjust {public,internal}_reserved. + The size of the class shouldn't change [unless you really are prepared to + invalidate all existing executables]. The program does a check (using + SIZEOF_PER_PROCESS) to make sure you remember to make the adjustment. +*/ + +#ifdef __cplusplus +class MTinterface; +#endif + +struct per_process +{ + char *initial_sp; + + /* The offset of these 3 values can never change. */ + /* magic_biscuit is the size of this class and should never change. */ + unsigned long magic_biscuit; + unsigned long dll_major; + unsigned long dll_minor; + + struct _reent **impure_ptr_ptr; + char ***envptr; + + /* Used to point to the memory machine we should use. Usually these + point back into the dll, but they can be overridden by the user. */ + void *(*malloc)(size_t); + void (*free)(void *); + void *(*realloc)(void *, size_t); + + int *fmode_ptr; + + int (*main)(int, char **, char **); + void (**ctors)(void); + void (**dtors)(void); + + /* For fork */ + void *data_start; + void *data_end; + void *bss_start; + void *bss_end; + + void *(*calloc)(size_t, size_t); + /* For future expansion of values set by the app. */ + void (*premain[4]) (int, char **, struct per_process *); + + /* The rest are *internal* to cygwin.dll. + Those that are here because we want the child to inherit the value from + the parent (which happens when bss is copied) are marked as such. */ + + /* non-zero of ctors have been run. Inherited from parent. */ + int run_ctors_p; + + DWORD unused[7]; + + /* Non-zero means the task was forked. The value is the pid. + Inherited from parent. */ + int forkee; + + HMODULE hmodule; + + DWORD api_major; /* API version that this program was */ + DWORD api_minor; /* linked with */ + /* For future expansion, so apps won't have to be relinked if we + add an item. */ + DWORD unused2[6]; + +#if defined (__INSIDE_CYGWIN__) && defined (__cplusplus) + MTinterface *threadinterface; +#else + void *threadinterface; +#endif + struct _reent *impure_ptr; +}; +#define per_process_overwrite ((unsigned) &(((struct per_process *) NULL)->threadinterface)) + +extern void cygwin_premain0 (int argc, char **argv, struct per_process *); +extern void cygwin_premain1 (int argc, char **argv, struct per_process *); +extern void cygwin_premain2 (int argc, char **argv, struct per_process *); +extern void cygwin_premain3 (int argc, char **argv, struct per_process *); + +extern void cygwin_set_impersonation_token (const HANDLE); + +/* included if <windows.h> is included */ +extern int cygwin32_attach_handle_to_fd (char *, int, HANDLE, mode_t, DWORD); +extern int cygwin_attach_handle_to_fd (char *, int, HANDLE, mode_t, DWORD); + +#ifdef __CYGWIN__ +#include <sys/resource.h> + +#define TTY_CONSOLE 0x40000000 + +#define EXTERNAL_PINFO_VERSION_16_BIT 0 +#define EXTERNAL_PINFO_VERSION_32_BIT 1 +#define EXTERNAL_PINFO_VERSION EXTERNAL_PINFO_VERSION_32_BIT + +#ifndef _SYS_TYPES_H +typedef unsigned short __uid16_t; +typedef unsigned short __gid16_t; +typedef unsigned long __uid32_t; +typedef unsigned long __gid32_t; +#endif + +struct external_pinfo + { + pid_t pid; + pid_t ppid; + DWORD exitcode; + DWORD dwProcessId, dwSpawnedProcessId; + __uid16_t uid; + __gid16_t gid; + pid_t pgid; + pid_t sid; + int ctty; + mode_t umask; + + long start_time; + struct rusage rusage_self; + struct rusage rusage_children; + + char progname[MAX_PATH]; + + DWORD strace_mask; + DWORD version; + + DWORD process_state; + + /* Only available if version >= EXTERNAL_PINFO_VERSION_32_BIT */ + __uid32_t uid32; + __gid32_t gid32; +}; +#endif /*__CYGWIN__*/ +#endif /*WINVER*/ + +#ifdef __cplusplus +}; +#endif +#endif /* _SYS_CYGWIN_H */ diff --git a/winsup/cygwin/mmap.cc b/winsup/cygwin/mmap.cc index 88b98523339..ed8ee73994b 100644 --- a/winsup/cygwin/mmap.cc +++ b/winsup/cygwin/mmap.cc @@ -24,6 +24,7 @@ details. */ #include "pinfo.h" #include "sys/cygwin.h" #include "ntdll.h" +#include <sys/queue.h> /* __PROT_ATTACH indicates an anonymous mapping which is supposed to be attached to a file mapping for pages beyond the file's EOF. The idea @@ -50,6 +51,11 @@ details. */ /* Used for anonymous mappings. */ static fhandler_dev_zero fh_anonymous; +/* Used for thread synchronization while accessing mmap bookkeeping lists. */ +static NO_COPY muto mmap_guard; +#define LIST_LOCK() (mmap_guard.init ("mmap_guard")->acquire ()) +#define LIST_UNLOCK() (mmap_guard.release ()) + /* Small helpers to avoid having lots of flag bit tests in the code. */ static inline bool priv (int flags) @@ -422,16 +428,20 @@ mmap_init () The class structure: One member of class map per process, global variable mmapped_areas. - Contains a dynamic class list array. Each list entry represents all - mapping to a file, keyed by file descriptor and file name hash. - Each list entry contains a dynamic class mmap_record array. Each - mmap_record represents exactly one mapping. For each mapping, there's + Contains a singly-linked list of type class mmap_list. Each mmap_list + entry represents all mapping to a file, keyed by file descriptor and + file name hash. + Each list entry contains a singly-linked list of type class mmap_record. + Each mmap_record represents exactly one mapping. For each mapping, there's an additional so called `page_map'. It's an array of bits, one bit per mapped memory page. The bit is set if the page is accessible, unset otherwise. */ class mmap_record { + public: + LIST_ENTRY (mmap_record) mr_next; + private: int fd; HANDLE mapping_hdl; @@ -485,6 +495,7 @@ class mmap_record void free_page_map () { if (page_map) cfree (page_map); } DWORD find_unused_pages (DWORD pages) const; + bool match (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len); _off64_t map_pages (_off64_t off, DWORD len); bool map_pages (caddr_t addr, DWORD len); bool unmap_pages (caddr_t addr, DWORD len); @@ -502,45 +513,39 @@ class mmap_record bool compatible_flags (int fl) const; }; -class list +class mmap_list { + public: + LIST_ENTRY (mmap_list) ml_next; + LIST_HEAD (, mmap_record) recs; + private: - mmap_record *recs; - int nrecs, maxrecs; int fd; __ino64_t hash; public: int get_fd () const { return fd; } __ino64_t get_hash () const { return hash; } - mmap_record *get_record (int i) { return i >= nrecs ? NULL : recs + i; } bool anonymous () const { return fd == -1; } void set (int nfd); mmap_record *add_record (mmap_record r); - bool del_record (int i); - void free_recs () { if (recs) cfree (recs); } - mmap_record *search_record (_off64_t off, DWORD len); - long search_record (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len, - long start); + bool del_record (mmap_record *rec); caddr_t try_map (void *addr, size_t len, int flags, _off64_t off); }; -class map +class mmap_areas { - private: - list *lists; - unsigned nlists, maxlists; - public: - list *get_list (unsigned i) { return i >= nlists ? NULL : lists + i; } - list *get_list_by_fd (int fd); - list *add_list (int fd); - void del_list (unsigned i); + LIST_HEAD (, mmap_list) lists; + + mmap_list *get_list_by_fd (int fd); + mmap_list *add_list (int fd); + void del_list (mmap_list *ml); }; /* This is the global map structure pointer. */ -static map mmapped_areas; +static mmap_areas mmapped_areas; bool mmap_record::compatible_flags (int fl) const @@ -571,6 +576,25 @@ mmap_record::find_unused_pages (DWORD pages) const } bool +mmap_record::match (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len) +{ + caddr_t low = (addr >= get_address ()) ? addr : get_address (); + caddr_t high = get_address (); + if (filler ()) + high += get_len (); + else + high += (PAGE_CNT (get_len ()) * getsystempagesize ()); + high = (addr + len < high) ? addr + len : high; + if (low < high) + { + m_addr = low; + m_len = high - low; + return true; + } + return false; +} + +bool mmap_record::alloc_page_map () { /* Allocate one bit per page */ @@ -710,79 +734,23 @@ mmap_record::free_fh (fhandler_base *fh) } mmap_record * -list::add_record (mmap_record r) +mmap_list::add_record (mmap_record r) { - if (nrecs == maxrecs) - { - mmap_record *new_recs; - if (maxrecs == 0) - new_recs = (mmap_record *) - cmalloc (HEAP_MMAP, 5 * sizeof (mmap_record)); - else - new_recs = (mmap_record *) - crealloc (recs, (maxrecs + 5) * sizeof (mmap_record)); - if (!new_recs) - return NULL; - maxrecs += 5; - recs = new_recs; - } - recs[nrecs] = r; - if (!recs[nrecs].alloc_page_map ()) + mmap_record *rec = (mmap_record *) cmalloc (HEAP_MMAP, sizeof (mmap_record)); + if (!rec) return NULL; - return recs + nrecs++; -} - -/* Used in mmap() */ -mmap_record * -list::search_record (_off64_t off, DWORD len) -{ - if (anonymous () && !off) - { - len = PAGE_CNT (len); - for (int i = 0; i < nrecs; ++i) - if (recs[i].find_unused_pages (len) != (DWORD)-1) - return recs + i; - } - else + *rec = r; + if (!rec->alloc_page_map ()) { - for (int i = 0; i < nrecs; ++i) - if (off >= recs[i].get_offset () - && off + len - <= recs[i].get_offset () - + (PAGE_CNT (recs[i].get_len ()) * getsystempagesize ())) - return recs + i; - } - return NULL; -} - -/* Used in munmap() */ -long -list::search_record (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len, - long start) -{ - caddr_t low, high; - - for (long i = start + 1; i < nrecs; ++i) - { - low = (addr >= recs[i].get_address ()) ? addr : recs[i].get_address (); - high = recs[i].get_address (); - if (recs[i].filler ()) - high += recs[i].get_len (); - else - high += (PAGE_CNT (recs[i].get_len ()) * getsystempagesize ()); - high = (addr + len < high) ? addr + len : high; - if (low < high) - { - m_addr = low; - m_len = high - low; - return i; - } + cfree (rec); + return NULL; } - return -1; + LIST_INSERT_HEAD (&recs, rec, mr_next); + return rec; } void -list::set (int nfd) +mmap_list::set (int nfd) { fd = nfd; if (!anonymous ()) @@ -794,27 +762,22 @@ list::set (int nfd) fstat (nfd, &st); hash = st.st_ino; } - nrecs = maxrecs = 0; - recs = NULL; + LIST_INIT (&recs); } bool -list::del_record (int i) +mmap_list::del_record (mmap_record *rec) { - if (i < nrecs) - { - recs[i].free_page_map (); - for (; i < nrecs - 1; i++) - recs[i] = recs[i + 1]; - nrecs--; - } + rec->free_page_map (); + LIST_REMOVE (rec, mr_next); + cfree (rec); /* Return true if the list is empty which allows the caller to remove - this list from the list array. */ - return !nrecs; + this list from the list of lists. */ + return !LIST_FIRST(&recs); } caddr_t -list::try_map (void *addr, size_t len, int flags, _off64_t off) +mmap_list::try_map (void *addr, size_t len, int flags, _off64_t off) { mmap_record *rec; @@ -822,10 +785,13 @@ list::try_map (void *addr, size_t len, int flags, _off64_t off) { /* If MAP_FIXED isn't given, check if this mapping matches into the chunk of another already performed mapping. */ - if ((rec = search_record (off, len)) != NULL - && rec->compatible_flags (flags)) + DWORD plen = PAGE_CNT (len); + LIST_FOREACH (rec, &recs, mr_next) + if (rec->find_unused_pages (plen) != (DWORD) -1) + break; + if (rec && rec->compatible_flags (flags)) { - if ((off = rec->map_pages (off, len)) == (_off64_t)-1) + if ((off = rec->map_pages (off, len)) == (_off64_t) -1) return (caddr_t) MAP_FAILED; return (caddr_t) rec->get_address () + off; } @@ -837,11 +803,11 @@ list::try_map (void *addr, size_t len, int flags, _off64_t off) if a memory region is unmapped and remapped with MAP_FIXED. */ caddr_t u_addr; DWORD u_len; - long record_idx = -1; - if ((record_idx = search_record ((caddr_t) addr, len, u_addr, u_len, - record_idx)) >= 0) + LIST_FOREACH (rec, &recs, mr_next) + if (rec->match ((caddr_t) addr, len, u_addr, u_len)) + break; + if (rec) { - rec = get_record (record_idx); if (u_addr > (caddr_t) addr || u_addr + len < (caddr_t) addr + len || !rec->compatible_flags (flags)) { @@ -859,53 +825,40 @@ list::try_map (void *addr, size_t len, int flags, _off64_t off) return NULL; } -list * -map::get_list_by_fd (int fd) +mmap_list * +mmap_areas::get_list_by_fd (int fd) { - unsigned i; - for (i = 0; i < nlists; i++) + mmap_list *ml; + LIST_FOREACH (ml, &lists, ml_next) { - if (fd == -1 && lists[i].anonymous ()) - return lists + i; + if (fd == -1 && ml->anonymous ()) + return ml; /* The fd isn't sufficient since it could already be the fd of another file. So we use the inode number as evaluated by fstat to identify the file. */ struct stat st; - if (fd != -1 && !fstat (fd, &st) && lists[i].get_hash () == st.st_ino) - return lists + i; + if (fd != -1 && !fstat (fd, &st) && ml->get_hash () == st.st_ino) + return ml; } return 0; } -list * -map::add_list (int fd) +mmap_list * +mmap_areas::add_list (int fd) { - if (nlists == maxlists) - { - list *new_lists; - if (maxlists == 0) - new_lists = (list *) cmalloc (HEAP_MMAP, 5 * sizeof (list)); - else - new_lists = (list *) crealloc (lists, (maxlists + 5) * sizeof (list)); - if (!new_lists) - return NULL; - maxlists += 5; - lists = new_lists; - } - lists[nlists].set (fd); - return lists + nlists++; + mmap_list *ml = (mmap_list *) cmalloc (HEAP_MMAP, sizeof (mmap_list)); + if (!ml) + return NULL; + ml->set (fd); + LIST_INSERT_HEAD (&lists, ml, ml_next); + return ml; } void -map::del_list (unsigned i) +mmap_areas::del_list (mmap_list *ml) { - if (i < nlists) - { - lists[i].free_recs (); - for (; i < nlists - 1; i++) - lists[i] = lists[i + 1]; - nlists--; - } + LIST_REMOVE (ml, ml_next); + cfree (ml); } /* This function is called from exception_handler when a segmentation @@ -929,31 +882,42 @@ map::del_list (unsigned i) int mmap_is_attached_or_noreserve_page (ULONG_PTR addr) { - list *map_list; - long record_idx; + mmap_list *map_list; + mmap_record *rec; caddr_t u_addr; DWORD u_len; DWORD pagesize = getsystempagesize (); + int ret; addr = rounddown (addr, pagesize); + LIST_LOCK (); if (!(map_list = mmapped_areas.get_list_by_fd (-1))) - return 0; - if ((record_idx = map_list->search_record ((caddr_t)addr, pagesize, - u_addr, u_len, -1)) < 0) - return 0; - if (map_list->get_record (record_idx)->attached ()) - return 1; - if (!map_list->get_record (record_idx)->noreserve ()) - return 0; - DWORD new_prot = map_list->get_record (record_idx)->gen_protect (); - return VirtualAlloc ((void *)addr, pagesize, MEM_COMMIT, new_prot) ? 2 : 1; + ret = 0; + else + { + LIST_FOREACH (rec, &map_list->recs, mr_next) + if (rec->match ((caddr_t) addr, pagesize, u_addr, u_len)) + break; + if (!rec) + ret = 0; + else if (rec->attached ()) + ret = 1; + else if (!rec->noreserve ()) + ret = 0; + else + ret = VirtualAlloc ((void *)addr, pagesize, MEM_COMMIT, + rec->gen_protect ()) + ? 2 : 1; + } + LIST_UNLOCK (); + return ret; } static caddr_t mmap_worker (fhandler_base *fh, caddr_t base, size_t len, int prot, int flags, int fd, _off64_t off) { - list *map_list; + mmap_list *map_list; HANDLE h = fh->mmap (&base, len, prot, flags, off); if (h == INVALID_HANDLE_VALUE) return NULL; @@ -983,7 +947,7 @@ mmap64 (void *addr, size_t len, int prot, int flags, int fd, _off64_t off) fhandler_base *fh = NULL; fhandler_disk_file *fh_disk_file = NULL; /* Used for reopening a disk file when necessary. */ - list *map_list = NULL; + mmap_list *map_list = NULL; size_t orig_len = 0; caddr_t base = NULL; @@ -993,8 +957,6 @@ mmap64 (void *addr, size_t len, int prot, int flags, int fd, _off64_t off) fh_anonymous.set_io_handle (INVALID_HANDLE_VALUE); fh_anonymous.set_access (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE); - SetResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); - /* EINVAL error conditions. Note that the addr%pagesize test is deferred to workaround a serious alignment problem in Windows 98. */ if (off % pagesize @@ -1184,6 +1146,7 @@ go_ahead: if (noreserve (flags) && (!anonymous (flags) || !priv (flags))) flags &= ~MAP_NORESERVE; + LIST_LOCK (); map_list = mmapped_areas.get_list_by_fd (fd); /* Test if an existing anonymous mapping can be recycled. */ @@ -1195,7 +1158,7 @@ go_ahead: if (tried) { ret = tried; - goto out; + goto out_with_unlock; } } @@ -1204,7 +1167,7 @@ go_ahead: && fixed (flags) && ((uintptr_t) addr % pagesize)) { set_errno (EINVAL); - goto out; + goto out_with_unlock; } if (orig_len) @@ -1227,20 +1190,20 @@ go_ahead: if (!newaddr) { __seterrno (); - goto out; + goto out_with_unlock; } } if (!VirtualFree (newaddr, 0, MEM_RELEASE)) { __seterrno (); - goto out; + goto out_with_unlock; } addr = newaddr; } base = mmap_worker (fh, (caddr_t) addr, len, prot, flags, fd, off); if (!base) - goto out; + goto out_with_unlock; if (orig_len) { @@ -1276,7 +1239,7 @@ go_ahead: { fh->munmap (fh->get_handle (), base, len); set_errno (ENOMEM); - goto out; + goto out_with_unlock; } at_base += valid_page_len; } @@ -1294,9 +1257,10 @@ go_ahead: ret = base; -out: +out_with_unlock: + LIST_UNLOCK (); - ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); +out: if (fh_disk_file) CloseHandle (fh_disk_file->get_handle ()); @@ -1334,23 +1298,21 @@ munmap (void *addr, size_t len) } len = roundup2 (len, pagesize); - SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap"); + LIST_LOCK (); /* Iterate through the map, unmap pages between addr and addr+len in all maps. */ - list *map_list; - for (unsigned list_idx = 0; - (map_list = mmapped_areas.get_list (list_idx)); - ++list_idx) + mmap_list *map_list, *next_map_list; + LIST_FOREACH_SAFE (map_list, &mmapped_areas.lists, ml_next, next_map_list) { - long record_idx = -1; + mmap_record *rec, *next_rec; caddr_t u_addr; DWORD u_len; - while ((record_idx = map_list->search_record((caddr_t)addr, len, u_addr, - u_len, record_idx)) >= 0) + LIST_FOREACH_SAFE (rec, &map_list->recs, mr_next, next_rec) { - mmap_record *rec = map_list->get_record (record_idx); + if (!rec->match ((caddr_t) addr, len, u_addr, u_len)) + continue; if (rec->unmap_pages (u_addr, u_len)) { /* The whole record has been unmapped, so we now actually @@ -1362,18 +1324,18 @@ munmap (void *addr, size_t len) rec->free_fh (fh); /* ...and delete the record. */ - if (map_list->del_record (record_idx--)) + if (map_list->del_record (rec)) { /* Yay, the last record has been removed from the list, we can remove the list now, too. */ - mmapped_areas.del_list (list_idx--); + mmapped_areas.del_list (map_list); break; } } } } - ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap"); + LIST_UNLOCK (); syscall_printf ("0 = munmap(): %x", addr); return 0; } @@ -1384,11 +1346,11 @@ extern "C" int msync (void *addr, size_t len, int flags) { int ret = -1; - list *map_list; + mmap_list *map_list; syscall_printf ("msync (addr: %p, len %u, flags %x)", addr, len, flags); - SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync"); + LIST_LOCK (); /* See comment in mmap64 for a description. */ size_t pagesize = wincap.has_mmap_alignment_bug () ? @@ -1406,26 +1368,22 @@ msync (void *addr, size_t len, int flags) /* Iterate through the map, looking for the mmapped area. Error if not found. */ - for (unsigned list_idx = 0; - (map_list = mmapped_areas.get_list (list_idx)); - ++list_idx) + LIST_FOREACH (map_list, &mmapped_areas.lists, ml_next) { mmap_record *rec; - for (int record_idx = 0; - (rec = map_list->get_record (record_idx)); - ++record_idx) + LIST_FOREACH (rec, &map_list->recs, mr_next) { - if (rec->access ((caddr_t)addr)) + if (rec->access ((caddr_t) addr)) { /* Check whole area given by len. */ for (DWORD i = getpagesize (); i < len; i += getpagesize ()) - if (!rec->access ((caddr_t)addr + i)) + if (!rec->access ((caddr_t) addr + i)) { set_errno (ENOMEM); goto out; } fhandler_base *fh = rec->alloc_fh (); - ret = fh->msync (rec->get_handle (), (caddr_t)addr, len, flags); + ret = fh->msync (rec->get_handle (), (caddr_t) addr, len, flags); rec->free_fh (fh); goto out; } @@ -1437,7 +1395,7 @@ msync (void *addr, size_t len, int flags) out: syscall_printf ("%d = msync()", ret); - ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync"); + LIST_UNLOCK (); return ret; } @@ -1463,46 +1421,43 @@ mprotect (void *addr, size_t len, int prot) } len = roundup2 (len, pagesize); - SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "mprotect"); + LIST_LOCK (); /* Iterate through the map, protect pages between addr and addr+len in all maps. */ - list *map_list; - for (unsigned list_idx = 0; - (map_list = mmapped_areas.get_list (list_idx)); - ++list_idx) - { - long record_idx = -1; - caddr_t u_addr; - DWORD u_len; - - while ((record_idx = map_list->search_record((caddr_t)addr, len, - u_addr, u_len, - record_idx)) >= 0) - { - mmap_record *rec = map_list->get_record (record_idx); - in_mapped = true; - if (rec->attached ()) - continue; - new_prot = gen_protect (prot, rec->get_flags ()); - if (rec->noreserve ()) - { - if (new_prot == PAGE_NOACCESS) - ret = VirtualFree (u_addr, u_len, MEM_DECOMMIT); - else - ret = !!VirtualAlloc (u_addr, u_len, MEM_COMMIT, new_prot); - } - else - ret = VirtualProtect (u_addr, u_len, new_prot, &old_prot); - if (!ret) - { - __seterrno (); - break; - } - } - } - - ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "mprotect"); + mmap_list *map_list; + LIST_FOREACH (map_list, &mmapped_areas.lists, ml_next) + { + mmap_record *rec; + caddr_t u_addr; + DWORD u_len; + + LIST_FOREACH (rec, &map_list->recs, mr_next) + { + if (!rec->match ((caddr_t) addr, len, u_addr, u_len)) + continue; + in_mapped = true; + if (rec->attached ()) + continue; + new_prot = gen_protect (prot, rec->get_flags ()); + if (rec->noreserve ()) + { + if (new_prot == PAGE_NOACCESS) + ret = VirtualFree (u_addr, u_len, MEM_DECOMMIT); + else + ret = !!VirtualAlloc (u_addr, u_len, MEM_COMMIT, new_prot); + } + else + ret = VirtualProtect (u_addr, u_len, new_prot, &old_prot); + if (!ret) + { + __seterrno (); + break; + } + } + } + + LIST_UNLOCK (); if (!in_mapped) { @@ -1973,15 +1928,11 @@ int __stdcall fixup_mmaps_after_fork (HANDLE parent) { /* Iterate through the map */ - list *map_list; - for (unsigned list_idx = 0; - (map_list = mmapped_areas.get_list (list_idx)); - ++list_idx) + mmap_list *map_list; + LIST_FOREACH (map_list, &mmapped_areas.lists, ml_next) { mmap_record *rec; - for (int record_idx = 0; - (rec = map_list->get_record (record_idx)); - ++record_idx) + LIST_FOREACH (rec, &map_list->recs, mr_next) { debug_printf ("fd %d, h 0x%x, address %p, len 0x%x, prot: 0x%x, " "flags: 0x%x, offset %X", diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index 12fe5992271..73bb9b142f6 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -284,43 +284,6 @@ semaphore::is_good_object (sem_t const * sem) return true; } -LPCRITICAL_SECTION -ResourceLocks::Lock (int _resid) -{ - return &lock; -} - -void -SetResourceLock (int _res_id, int _mode, const char *_function) -{ - EnterCriticalSection (user_data->resourcelocks->Lock (_res_id)); -} - -void -ReleaseResourceLock (int _res_id, int _mode, const char *_function) -{ - LeaveCriticalSection (user_data->resourcelocks->Lock (_res_id)); -} - -void -ResourceLocks::Init () -{ - InitializeCriticalSection (&lock); - inited = true; - thread_printf ("lock %p inited by %p , %d", &lock, user_data, myself->pid); -} - -void -ResourceLocks::Delete () -{ - if (inited) - { - thread_printf ("Close Resource Locks %p ", &lock); - DeleteCriticalSection (&lock); - inited = false; - } -} - void MTinterface::Init () { diff --git a/winsup/cygwin/thread.h b/winsup/cygwin/thread.h new file mode 100644 index 00000000000..ed2ec540e71 --- /dev/null +++ b/winsup/cygwin/thread.h @@ -0,0 +1,687 @@ +/* thread.h: Locking and threading module definitions + + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 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 _THREAD_H +#define _THREAD_H + +#define LOCK_MMAP_LIST 1 + +#define WRITE_LOCK 1 +#define READ_LOCK 2 + +#include <pthread.h> +#include <limits.h> +#include <security.h> +#include <errno.h> + +enum cw_sig_wait +{ + cw_sig_nosig, + cw_sig_eintr, + cw_sig_resume +}; + +enum cw_cancel_action +{ + cw_cancel_self, + cw_no_cancel_self, + cw_no_cancel +}; + +DWORD cancelable_wait (HANDLE, DWORD, const cw_cancel_action = cw_cancel_self, const enum cw_sig_wait = cw_sig_nosig) + __attribute__ ((regparm (3))); + +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) + cancelable_wait (win32_obj_id, INFINITE, cw_no_cancel, cw_sig_resume); + } + + 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; + +#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; + +template <class list_node> inline void +List_insert (list_node *&head, list_node *node) +{ + if (!node) + return; + do + node->next = head; + while (InterlockedCompareExchangePointer (&head, node, node->next) != node->next); +} + +template <class list_node> inline void +List_remove (fast_mutex &mx, list_node *&head, list_node const *node) +{ + if (!node) + return; + mx.lock (); + if (head) + { + if (InterlockedCompareExchangePointer (&head, node->next, node) != node) + { + 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 (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 +{ + DWORD tls_index; +public: + static bool is_good_object (pthread_key_t const *); + + int set (const void *value) {TlsSetValue (tls_index, (void *) value); return 0;} + void *get () const {return TlsGetValue (tls_index);} + + 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 *mutex, const pthread_mutexattr_t *attr, + const pthread_mutex_t initializer = NULL); + + 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) +#define WAIT_SIGNALED (WAIT_OBJECT_0 + 2) + +class _cygtls; +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; + _cygtls *cygtls; + HANDLE cancel_event; + pthread_t joiner; + + virtual bool 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) __attribute__ ((noreturn)); + + virtual int cancel (); + + virtual void testcancel (); + static void static_cancel_self (); + + 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 DWORD WINAPI 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 precreate (pthread_attr *); + void postcreate (); + bool create_cancel_event (); + static pthread *get_tls_self_pointer (); + static void set_tls_self_pointer (pthread *); + void cancel_self (); + DWORD get_thread_id (); +}; + +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 + */ + bool create (void *(*)(void *), pthread_attr *, void *); + void exit (void *value_ptr) __attribute__ ((noreturn)); + 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: + int _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; +}; + +struct MTinterface +{ + // General + int concurrency; + long int threadcount; + + callback *pthread_prepare; + callback *pthread_child; + callback *pthread_parent; + + void Init (); + void fixup_before_fork (); + void fixup_after_fork (); + +#if 0 // avoid initialization since zero is implied and + MTinterface () : + concurrency (0), threadcount (0), + pthread_prepare (NULL), pthread_child (NULL), pthread_parent (NULL) + { + } +#endif +}; + +#define MT_INTERFACE user_data->threadinterface +#endif // _THREAD_H |