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