1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2017 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5  *
6  * This software is provided 'as-is', without any express or implied warranty.
7  * In no event will the authors be held liable for any damages arising from the
8  * use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not claim
15  * that you wrote the original software. If you use this software in a product,
16  * an acknowledgment in the product documentation would be appreciated but is
17  * not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  * misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source distribution
23  */
24 
25 /**
26  * $(U Mutex) stands for "MUTual EXclusion". A mutex is a synchronization
27  * object, used when multiple threads are involved.
28  *
29  * When you want to protect a part of the code from being accessed
30  * simultaneously by multiple threads, you typically use a mutex. When a thread
31  * is locked by a mutex, any other thread trying to lock it will be blocked
32  * until the mutex is released by the thread that locked it. This way, you can
33  * allow only one thread at a time to access a critical region of your code.
34  *
35  * Example:
36  * ---
37  * // this is a critical resource that needs some protection
38  * Database database;
39  * auto mutex = Mutex();
40  *
41  * void thread1()
42  * {
43  * 	   // this call will block the thread if the mutex is already locked by thread2
44  *     mutex.lock();
45  *     database.write(...);
46  *	   // if thread2 was waiting, it will now be unblocked
47  *     mutex.unlock();
48  * }
49  *
50  * void thread2()
51  * {
52  *     // this call will block the thread if the mutex is already locked by thread1
53  *     mutex.lock();
54  *     database.write(...);
55  *     mutex.unlock(); // if thread1 was waiting, it will now be unblocked
56  * }
57  * ---
58  *
59  * $(PARA Be very careful with mutexes. A bad usage can lead to bad problems,
60  * like deadlocks (two threads are waiting for each other and the application is
61  * globally stuck).
62  *
63  * To make the usage of mutexes more robust, particularly in environments where
64  * exceptions can be thrown, you should use the helper class $(LOCK_LINK) to
65  * lock/unlock mutexes.
66  *
67  * DSFML mutexes are recursive, which means that you can lock a mutex multiple
68  * times in the same thread without creating a deadlock. In this case, the first
69  * call to `lock()` behaves as usual, and the following ones have no effect.
70  * However, you must call `unlock()` exactly as many times as you called
71  * `lock()`. If you don't, the mutex won't be released.
72  *
73  * Note that the $(U Mutex) class is added for convenience, and is nothing more
74  * than a simnple wrapper around the existing core.sync.mutex.Mutex class.)
75  *
76  * See_Also:
77  * $(LOCK_LINK)
78  */
79 module dsfml.system.mutex;
80 
81 import core = core.sync.mutex;
82 
83 /**
84  * Blocks concurrent access to shared resources from multiple threads.
85  */
86 class Mutex
87 {
88 	private core.Mutex m_mutex;
89 
90 	/// Default Constructor
91 	this()
92 	{
93 		m_mutex = new core.Mutex();
94 	}
95 
96 	/// Destructor
97 	~this()
98 	{
99 		import dsfml.system.config;
100 		mixin(destructorOutput);
101 	}
102 
103 	/**
104 	 * Lock the mutex
105 	 *
106 	 * If the mutex is already locked in another thread, this call will block
107 	 * the execution until the mutex is released.
108 	 */
109 	void lock()
110 	{
111 		m_mutex.lock();
112 	}
113 
114 	/// Unlock the mutex
115 	void unlock()
116 	{
117 		m_mutex.unlock();
118 	}
119 }
120 
121 
122 unittest
123 {
124 	version(DSFML_Unittest_System)
125 	{
126 		import dsfml.system.thread;
127 		import dsfml.system.sleep;
128 		import core.time;
129 		import std.stdio;
130 
131 		auto mutex = new Mutex();
132 
133 		void secondThreadHello()
134 		{
135 			mutex.lock();
136 			for(int i = 0; i < 10; ++i)
137 			{
138 				writeln("Hello from the second thread!");
139 			}
140 			mutex.unlock();
141 		}
142 
143 
144 		writeln("Unit test for Mutex class");
145 		writeln();
146 
147 		writeln("Locking a mutex and then unlocking it later.");
148 
149 		auto secondThread = new Thread(&secondThreadHello);
150 
151 		secondThread.launch();
152 
153 		mutex.lock();
154 
155 		for(int i = 0; i < 10; ++i)
156 		{
157 			writeln("Hello from the main thread!");
158 		}
159 
160 		mutex.unlock();
161 		sleep(seconds(1));//let's this unit test finish before moving on to the next one.
162 		writeln();
163 	}
164 }