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