1 /*******************************************************************************
2   copyright:   Copyright (c) 2007 Juan Jose Comellas. All rights reserved
3   license:     BSD style: $(LICENSE)
4   author:      Juan Jose Comellas <juanjo@comellas.com.ar>
5                Converted to use core.sync by Sean Kelly <sean@f4.ca>
6 *******************************************************************************/
7 
8 module semaphore;
9 
10 private import tango.core.sync.Semaphore;
11 private import tango.core.sync.Mutex;
12 private import tango.core.Exception;
13 private import tango.core.Exception;
14 private import tango.core.Thread;
15 private import tango.io.Console;
16 private import tango.io.stream.Lines;
17 private import tango.text.convert.Integer;
18 private import tango.sys.Process;
19 
20 debug (semaphore)
21 {
22     private import tango.util.log.Log;
23     private import tango.util.log.AppendConsole;
24     private import tango.util.log.LayoutDate;
25 }
26 
27 const char[] SemaphoreName = "TestProcessSemaphore";
28 
29 
30 /**
31  * Example program for the tango.core.sync.Barrier module.
32  */
33 int main(char[][] args)
34 {
35     if (args.length == 1)
36     {
37         debug (semaphore)
38         {
39             Logger log = Log.getLogger("semaphore");
40 
41             log.add(new AppendConsole(new LayoutDate()));
42 
43             log.info("Semaphore test");
44         }
45 
46         testSemaphore();
47         testProcessSemaphore(args[0]);
48 
49         return 0;
50     }
51     else
52     {
53         return testSecondProcessSemaphore();
54     }
55 }
56 
57 /**
58  * Test for single-process (multi-threaded) semaphores.
59  */
60 void testSemaphore()
61 {
62     const uint MaxThreadCount   = 10;
63 
64     // Semaphore used in the tests.  Start it "locked" (i.e., its initial
65     // count is 0).
66     Semaphore   sem = new Semaphore(MaxThreadCount - 1);
67     Mutex       mutex = new Mutex();
68     uint        count = 0;
69     bool        passed = false;
70 
71     void semaphoreTestThread()
72     {
73         debug (semaphore)
74         {
75             Logger log = Log.getLogger("semaphore.single." ~ Thread.getThis().name());
76 
77             log.trace("Starting thread");
78         }
79 
80         try
81         {
82             uint threadNumber;
83 
84             // 'count' is a resource shared by multiple threads, so we must
85             // acquire the mutex before modifying it.
86             synchronized (mutex)
87             {
88                 // debug (semaphore)
89                 //     log.trace("Acquired mutex");
90                 threadNumber = ++count;
91                 // debug (semaphore)
92                 //     log.trace("Releasing mutex");
93             }
94 
95             // We wait for all the threads to finish counting.
96             if (threadNumber < MaxThreadCount)
97             {
98                 sem.wait();
99                 debug (semaphore)
100                     log.trace("Acquired semaphore");
101 
102                 while (true)
103                 {
104                     synchronized (mutex)
105                     {
106                         if (count >= MaxThreadCount + 1)
107                             break;
108                     }
109                     Thread.yield();
110                 }
111 
112                 debug (semaphore)
113                     log.trace("Releasing semaphore");
114                 sem.notify();
115             }
116             else
117             {
118                 passed = !sem.tryWait();
119                 if (passed)
120                 {
121                     debug (semaphore)
122                         log.trace("Tried to acquire the semaphore too many times and failed: OK");
123                 }
124                 else
125                 {
126                     debug (semaphore)
127                         log.error("Tried to acquire the semaphore too may times and succeeded: FAILED");
128 
129                     debug (semaphore)
130                         log.trace("Releasing semaphore");
131                     sem.notify();
132                 }
133                 synchronized (mutex)
134                 {
135                     count++;
136                 }
137             }
138         }
139         catch (tango.core.Exception.SyncException e)
140         {
141             Cerr("Sync exception caught in Semaphore test thread " ~ Thread.getThis().name ~
142                  ":\n" ~ e.toString()).newline;
143         }
144         catch (Exception e)
145         {
146             Cerr("Unexpected exception caught in Semaphore test thread " ~ Thread.getThis().name ~
147                  ":\n" ~ e.toString()).newline;
148         }
149     }
150 
151     debug (semaphore)
152     {
153         Logger log = Log.getLogger("semaphore.single");
154     }
155 
156     ThreadGroup group = new ThreadGroup();
157     Thread      thread;
158     char[10]    tmp;
159 
160     for (uint i = 0; i < MaxThreadCount; ++i)
161     {
162         thread = new Thread(&semaphoreTestThread);
163         thread.name = "thread-" ~ tango.text.convert.Integer.format(tmp, i).idup;
164 
165         group.add(thread);
166         debug (semaphore)
167             log.trace("Created thread " ~ thread.name);
168         thread.start();
169     }
170 
171     debug (semaphore)
172         log.trace("Waiting for threads to finish");
173     group.joinAll();
174 
175     if (passed)
176     {
177         debug (semaphore)
178             log.info("The Semaphore test was successful");
179     }
180     else
181     {
182         debug (semaphore)
183         {
184             log.error("The Semaphore is not working properly: it allowed "
185                       "to be acquired more than it should have done");
186             assert(false);
187         }
188         else
189         {
190             assert(false, "The Semaphore is not working properly: it allowed "
191                           "to be acquired more than it should have done");
192         }
193     }
194 }
195 
196 /**
197  * Test for multi-process semaphores: this test works by creating a copy of
198  * this process that tries to acquire the ProcessSemaphore that was created
199  * in this function. If everything works as expected, the attempt should fail,
200  * as the count of the semaphore is set to 1.
201  */
202 void testProcessSemaphore(char[] programName)
203 {
204     /+
205     bool success = false;
206 
207     debug (semaphore)
208     {
209         Logger log = Log.getLogger("semaphore.multi");
210         Logger childLog = Log.getLogger("semaphore.multi.child");
211 
212         log.info("ProcessSemaphore test");
213     }
214 
215     try
216     {
217         scope ProcessSemaphore sem = new ProcessSemaphore(SemaphoreName, 1);
218         Process proc = new Process(programName, "2");
219 
220         debug (semaphore)
221             log.trace("Created ProcessSemaphore('" ~ SemaphoreName ~ "')'");
222 
223         sem.wait();
224         debug (semaphore)
225             log.trace("Acquired semaphore in main process");
226 
227         debug (semaphore)
228             log.trace("Executing child test process: " ~ proc.toString());
229         proc.execute();
230 
231         debug (semaphore)
232         {
233             foreach (line; new Lines!(char)(proc.stdout))
234             {
235                 childLog.trace(line);
236             }
237         }
238         foreach (line; new Lines!(char)(proc.stderr))
239         {
240             Cerr(line).newline;
241         }
242 
243         debug (semaphore)
244             log.trace("Waiting for child process to finish");
245         auto result = proc.wait();
246 
247         success = (result.reason == Process.Result.Exit && result.status == 2);
248 
249         debug (semaphore)
250             log.trace("Releasing semaphore in main process");
251         sem.notify();
252     }
253     catch (tango.core.Exception.SyncException e)
254     {
255         Cerr("Sync exception caught in ProcessSemaphore main test process:\n" ~ e.toString()).newline;
256     }
257     catch (ProcessException e)
258     {
259         Cerr("Process exception caught in ProcessSemaphore main test process:\n" ~ e.toString()).newline;
260     }
261     catch (Exception e)
262     {
263         Cerr("Unexpected exception caught in ProcessSemaphore main test process:\n" ~ e.toString()).newline;
264     }
265 
266     if (success)
267     {
268         debug (semaphore)
269             log.info("The ProcessSemaphore test was successful");
270     }
271     else
272     {
273         debug (semaphore)
274         {
275             log.error("The multi-process semaphore is not working");
276             assert(false);
277         }
278         else
279         {
280             assert(false, "The multi-process semaphore is not working");
281         }
282     }
283     +/
284 }
285 
286 /**
287  * Test for multi-process semaphores (second process).
288  */
289 int testSecondProcessSemaphore()
290 {
291     int rc = 0;
292 
293     /+
294     debug (semaphore)
295     {
296         Cout("Starting child process\n");
297     }
298 
299     try
300     {
301         scope ProcessSemaphore sem = new ProcessSemaphore(SemaphoreName);
302         bool success;
303 
304         success = !sem.tryAcquire();
305         if (success)
306         {
307             debug (semaphore)
308                 Cout("Tried to acquire semaphore in child process and failed: OK\n");
309             rc = 2;
310         }
311         else
312         {
313             debug (semaphore)
314             {
315                 Cout("Acquired semaphore in child process: this should not have happened\n");
316                 Cout("Releasing semaphore in child process\n");
317             }
318             sem.notify();
319             rc = 1;
320         }
321     }
322     catch (tango.core.Exception.SyncException e)
323     {
324         Cerr("Sync exception caught in ProcessSemaphore child test process:\n" ~ e.toString()).newline;
325     }
326     catch (ProcessException e)
327     {
328         Cerr("Process exception caught in ProcessSemaphore child test process:\n" ~ e.toString()).newline;
329     }
330     catch (Exception e)
331     {
332         Cerr("Unexpected exception caught in ProcessSemaphore child test process:\n" ~ e.toString()).newline;
333     }
334 
335     debug (semaphore)
336         Cout("Leaving child process\n");
337 
338     +/
339     return rc;
340 }