OSDN Git Service

Rename cygWFMO to cygwait throughout and use the magic of polymorphism to "wait
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / sync.cc
1 /* sync.cc: Synchronization functions for cygwin.
2
3    This file implements the methods for controlling the "muto" class
4    which is intended to operate similarly to a mutex but attempts to
5    avoid making expensive calls to the kernel.
6
7    Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2010 Red Hat, Inc.
8
9 This file is part of Cygwin.
10
11 This software is a copyrighted work licensed under the terms of the
12 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
13 details. */
14
15 #include "winsup.h"
16 #include "miscfuncs.h"
17 #include "sync.h"
18 #include "thread.h"
19 #include "cygtls.h"
20
21 #undef WaitForSingleObject
22
23 muto NO_COPY lock_process::locker;
24
25 void
26 muto::grab ()
27 {
28   tls = &_my_tls;
29 }
30
31 /* Constructor */
32 muto *
33 muto::init (const char *s)
34 {
35   char *already_exists = (char *) InterlockedExchangePointer (&name, s);
36   if (already_exists)
37     while (!bruteforce)
38       yield ();
39   else
40     {
41       waiters = -1;
42       /* Create event which is used in the fallback case when blocking is necessary */
43       bruteforce = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
44       if (!bruteforce)
45           api_fatal ("couldn't allocate muto '%s', %E", s);
46     }
47
48   return this;
49 }
50
51 #if 0 /* FIXME: Do we need this? mutos aren't destroyed until process exit */
52 /* Destructor (racy?) */
53 muto::~muto ()
54 {
55   while (visits)
56     release ();
57
58   HANDLE h = bruteforce;
59   bruteforce = NULL;
60   /* Just need to close the event handle */
61   if (h)
62     CloseHandle (h);
63 }
64 #endif
65
66 /* Acquire the lock.  Argument is the number of milliseconds to wait for
67    the lock.  Multiple visits from the same thread are allowed and should
68    be handled correctly.
69
70    Note: The goal here is to minimize, as much as possible, calls to the
71    OS.  Hence the use of InterlockedIncrement, etc., rather than (much) more
72    expensive OS mutexes.  */
73 int
74 muto::acquire (DWORD ms)
75 {
76   void *this_tls = &_my_tls;
77
78   if (tls != this_tls)
79     {
80       /* Increment the waiters part of the class.  Need to do this first to
81          avoid potential races. */
82       LONG was_waiting = ms ? InterlockedIncrement (&waiters) : 0;
83
84       while (was_waiting || InterlockedExchange (&sync, 1) != 0)
85         switch (WaitForSingleObject (bruteforce, ms))
86             {
87             case WAIT_OBJECT_0:
88               was_waiting = 0;
89               break;
90             default:
91               return 0; /* failed. */
92             }
93
94       /* Have to do it this way to avoid a race */
95       if (!ms)
96         InterlockedIncrement (&waiters);
97
98       tls = this_tls;   /* register this thread. */
99     }
100
101   return ++visits;      /* Increment visit count. */
102 }
103
104 bool
105 muto::acquired ()
106 {
107   return tls == &_my_tls;
108 }
109
110 /* Return the muto lock.  Needs to be called once per every acquire. */
111 int
112 muto::release ()
113 {
114   void *this_tls = &_my_tls;
115
116   if (tls != this_tls || !visits)
117     {
118       SetLastError (ERROR_NOT_OWNER);   /* Didn't have the lock. */
119       return 0; /* failed. */
120     }
121
122   /* FIXME: Need to check that other thread has not exited, too. */
123   if (!--visits)
124     {
125       tls = 0;          /* We were the last unlocker. */
126       InterlockedExchange (&sync, 0); /* Reset trigger. */
127       /* This thread had incremented waiters but had never decremented it.
128          Decrement it now.  If it is >= 0 then there are possibly other
129          threads waiting for the lock, so trigger bruteforce.  */
130       if (InterlockedDecrement (&waiters) >= 0)
131         SetEvent (bruteforce); /* Wake up one of the waiting threads */
132       else if (*name == '!')
133         {
134           CloseHandle (bruteforce);     /* If *name == '!' and there are no
135                                            other waiters, then this is the
136                                            last time this muto will ever be
137                                            used, so close the handle. */
138 #ifdef DEBUGGING
139           bruteforce = NULL;
140 #endif
141         }
142     }
143
144   return 1;     /* success. */
145 }