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 }