summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <vinschen@redhat.com>2007-11-29 11:14:50 +0000
committerCorinna Vinschen <vinschen@redhat.com>2007-11-29 11:14:50 +0000
commitaaffc08d19b318f1d3923ec8d3a648d84948dc51 (patch)
tree364a4258f1ca9c92a8262069b43fc620f8d72c96
parentaaf339cf59104389ec9256c0226ab83c87c087c5 (diff)
downloadgdb-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/ChangeLog52
-rw-r--r--winsup/cygwin/dcrt0.cc6
-rw-r--r--winsup/cygwin/exceptions.cc3
-rw-r--r--winsup/cygwin/include/sys/cygwin.h271
-rw-r--r--winsup/cygwin/mmap.cc417
-rw-r--r--winsup/cygwin/thread.cc37
-rw-r--r--winsup/cygwin/thread.h687
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