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 Lock) is a RAII wrapper for DSFML's Mutex.
27  *
28  * By unlocking it in its destructor, it ensures that the mutex will always be
29  * released when the current scope (most likely a function) ends. This is even
30  * more important when an exception or an early return statement can interrupt
31  * the execution flow of the function.
32  *
33  * For maximum robustness, $(U Lock) should always be used to lock/unlock a
34  * mutex.
35  *
36  * Note that this structure is provided for convenience when porting projects
37  * from SFML to DSFML. The same effect can be achieved with scope guards and
38  * Mutex.
39  *
40  * Example:
41  * ---
42  * auto mutex = Mutex();
43  *
44  * void function()
45  * {
46  *     auto lock = Lock(mutex); // mutex is now locked
47  *
48  *	   // mutex is unlocked if this function throws
49  *     functionThatMayThrowAnException();
50  *
51  *     if (someCondition)
52  *         return; // mutex is unlocked
53  *
54  * } // mutex is unlocked
55  * ---
56  *
57  * $(PARA Because the mutex is not explicitly unlocked in the code, it may
58  * remain locked longer than needed. If the region of the code that needs to be
59  * protected by the mutex is not the entire function, a good practice is to
60  * create a smaller, inner scope so that the lock is limited to this part of the
61  * code.)
62  *
63  * Example:
64  * ---
65  * auto mutex = Mutex();
66  *
67  * void function()
68  * {
69  *     {
70  *       auto lock = Lock(mutex);
71  *       codeThatRequiresProtection();
72  *
73  *     } // mutex is unlocked here
74  *
75  *     codeThatDoesntCareAboutTheMutex();
76  * }
77  * ---
78  *
79  * $(PARA Having a mutex locked longer than required is a bad practice which can
80  * lead to bad performances. Don't forget that when a mutex is locked, other
81  * threads may be waiting doing nothing until it is released.)
82  *
83  * See_Also:
84  * $(MUTEX_LINK)
85  */
86 module dsfml.system.lock;
87 
88 import dsfml.system.mutex;
89 
90 /**
91 * Automatic wrapper for locking and unlocking mutexes.
92 */
93 struct Lock
94 {
95 	private Mutex m_mutex;
96 
97 	/**
98 	 * Construct the lock with a target mutex.
99 	 *
100 	 * The mutex passed to Lock is automatically locked.
101 	 *
102 	 * Params:
103 	 *   	mutex =	Mutex to lock
104 	 */
105 	this(Mutex mutex)
106 	{
107 		m_mutex = mutex;
108 
109 		m_mutex.lock();
110 	}
111 
112 	/// Destructor
113 	~this()
114 	{
115 		m_mutex.unlock();
116 	}
117 }
118 
119 unittest
120 {
121 	version(DSFML_Unittest_System)
122 	{
123 		import dsfml.system.thread;
124 		import dsfml.system.mutex;
125 		import dsfml.system.sleep;
126 		import core.time;
127 		import std.stdio;
128 
129 		Mutex mutex = new Mutex();
130 
131 		void mainThreadHello()
132 		{
133 			auto lock = Lock(mutex);
134 			for(int i = 0; i < 10; ++i)
135 			{
136 				writeln("Hello from the main thread!");
137 			}
138 			//unlock auto happens here
139 		}
140 		void secondThreadHello()
141 		{
142 			auto lock = Lock(mutex);
143 			for(int i = 0; i < 10; ++i)
144 			{
145 				writeln("Hello from the second thread!");
146 			}
147 			//unlock auto happens here
148 		}
149 
150 
151 		writeln("Unit test for Lock struct");
152 		writeln();
153 
154 		writeln("Using a lock in the main and second thread.");
155 
156 		auto secondThread = new Thread(&secondThreadHello);
157 
158 		secondThread.launch();
159 
160 		mainThreadHello();
161 
162 		//let this unit test finish before moving on to the next one
163 		sleep(seconds(1));
164 		writeln();
165 	}
166 }