Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / misc / p_thread.cpp
blob19a0cd1ee78665f453efb9b844b2672267eb0177
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2011 Robert TIMM (rti) <mail@rtti.de>
6 // Copyright (C) 2012-2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdmisc.h"
24 #include "nel/misc/types_nl.h"
25 #include "nel/misc/debug.h"
27 #ifdef NL_OS_UNIX
29 #include "nel/misc/p_thread.h"
31 #include <sched.h>
32 #include <pwd.h>
34 #ifdef DEBUG_NEW
35 #define new DEBUG_NEW
36 #endif
38 namespace NLMISC {
40 /* Key for thread specific storage holding IThread pointer. */
41 static pthread_key_t threadSpecificKey;
43 /* Special thread type representing the main thread. */
44 struct CPMainThread : public CPThread
46 CPMainThread() : CPThread(NULL, 0)
48 if(pthread_key_create(&threadSpecificKey, NULL) != 0)
49 throw EThread("cannot create thread specific storage key.");
51 if(pthread_setspecific(threadSpecificKey, this) != 0)
52 throw EThread("cannot set main thread ptr in thread specific storage.");
55 ~CPMainThread()
57 if (pthread_key_delete(threadSpecificKey) != 0)
59 nlwarning("cannot delete thread specific storage key.");
60 // throw EThread("cannot delete thread specific storage key.");
65 /* Holds the thread instance representing the main thread. */
66 static CPMainThread mainThread = CPMainThread();
69 * The IThread static creator
71 IThread *IThread::create( IRunnable *runnable, uint32 stackSize)
73 return new CPThread( runnable, stackSize );
77 * Get the current thread
79 IThread *IThread::getCurrentThread ()
81 return (IThread *)pthread_getspecific(threadSpecificKey);
85 * Thread beginning
87 static void *ProxyFunc( void *arg )
89 CPThread *parent = (CPThread*)arg;
91 // Set this thread's thread specific storage to IThread instance pointer
92 if(pthread_setspecific(threadSpecificKey, parent) != 0)
93 throw EThread("cannot set thread ptr in thread specific storage.");
95 // Allow to terminate the thread without cancellation point
96 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
98 // Run the code of the thread
99 parent->Runnable->run();
102 pthread_t thread_self = pthread_self();
103 // Make sure the parent still cares
104 // If this thread was replaced with a new thread (which should not happen),
105 // and the IThread object has been deleted, this will likely crash.
106 if (parent->_ThreadHandle == thread_self)
107 parent->_State = CPThread::ThreadStateFinished;
108 else
109 throw EThread("Thread ended after being detached, this should not happen");
112 // Allow some clean
113 // pthread_exit(0);
114 return NULL;
120 * Constructor
122 CPThread::CPThread(IRunnable *runnable, uint32 stackSize)
123 : Runnable(runnable),
124 _State(ThreadStateNone),
125 _StackSize(stackSize)
130 * Destructor
132 CPThread::~CPThread()
134 terminate(); // force the end of the thread if not already ended
136 if (_State != ThreadStateNone)
137 pthread_detach(_ThreadHandle); // free allocated resources only if it was created
141 * start
143 void CPThread::start()
145 pthread_attr_t tattr;
146 int ret;
148 if (_StackSize != 0)
150 /* initialized with default attributes */
151 ret = pthread_attr_init(&tattr);
153 /* setting the size of the stack also */
154 ret = pthread_attr_setstacksize(&tattr, _StackSize);
157 bool detach_old_thread = false;
158 pthread_t old_thread_handle = _ThreadHandle;
159 if (_State != ThreadStateNone)
161 if (_State == ThreadStateRunning)
163 // I don't know if this behaviour is allowed, but neither thread implementations
164 // check the start function, and both simply let the existing running thread for what it is...
165 // From now on, this is not allowed.
166 throw EThread("Starting a thread that is already started, existing thread will continue running, this should not happen");
168 detach_old_thread = true;
171 if (pthread_create(&_ThreadHandle, _StackSize != 0 ? &tattr : NULL, ProxyFunc, this) != 0)
173 throw EThread("Cannot start new thread");
175 _State = ThreadStateRunning;
177 if (detach_old_thread)
179 // Docs don't say anything about what happens when pthread_create is called with existing handle referenced.
180 if (old_thread_handle == _ThreadHandle)
181 throw EThread("Thread handle did not change, this should not happen");
182 // Don't care about old thread, free resources when it terminates.
183 pthread_detach(old_thread_handle);
187 bool CPThread::isRunning()
189 return _State == ThreadStateRunning;
193 * terminate
195 void CPThread::terminate()
197 if (_State == ThreadStateRunning)
199 // cancel only if started
200 pthread_cancel(_ThreadHandle);
201 _State = ThreadStateFinished; // set to finished
206 * wait
208 void CPThread::wait ()
210 if (_State == ThreadStateRunning)
212 int error = pthread_join(_ThreadHandle, 0);
213 switch (error)
215 case 0:
216 break;
217 case EINVAL:
218 throw EThread("Thread is not joinable");
219 case ESRCH:
220 throw EThread("No thread found with this id");
221 case EDEADLK:
222 throw EThread("Deadlock detected or calling thread waits for itself");
223 default:
224 throw EThread("Unknown thread join error");
226 if(_State != ThreadStateFinished)
227 throw EThread("Thread did not finish, this should not happen");
232 * setCPUMask
234 bool CPThread::setCPUMask(uint64 cpuMask)
236 #ifdef __USE_GNU
238 nlwarning("This code does not work. May cause a segmentation fault...");
240 sint res = pthread_setaffinity_np(_ThreadHandle, sizeof(uint64), (const cpu_set_t*)&cpuMask);
242 if (res)
244 nlwarning("pthread_setaffinity_np() returned %d", res);
245 return false;
248 return true;
250 #else // __USE_GNU
252 return false;
254 #endif // __USE_GNU
258 * getCPUMask
260 uint64 CPThread::getCPUMask()
262 #ifdef __USE_GNU
264 nlwarning("This code does not work. May cause a segmentation fault...");
266 uint64 cpuMask = 0;
268 sint res = pthread_getaffinity_np(_ThreadHandle, sizeof(uint64), (cpu_set_t*)&cpuMask);
270 if (res)
272 nlwarning("pthread_getaffinity_np() returned %d", res);
273 return 0;
276 return cpuMask;
278 #else // __USE_GNU
280 return 0;
282 #endif // __USE_GNU
285 void CPThread::setPriority(TThreadPriority priority)
287 // TODO: Test this
288 sched_param sp;
289 switch (priority)
291 case ThreadPriorityHigh:
293 int minPrio = sched_get_priority_min(SCHED_FIFO);
294 int maxPrio = sched_get_priority_max(SCHED_FIFO);
295 sp.sched_priority = ((maxPrio - minPrio) / 4) + minPrio;
296 pthread_setschedparam(_ThreadHandle, SCHED_FIFO, &sp);
297 break;
299 case ThreadPriorityHighest:
301 int minPrio = sched_get_priority_min(SCHED_FIFO);
302 int maxPrio = sched_get_priority_max(SCHED_FIFO);
303 sp.sched_priority = ((maxPrio - minPrio) / 2) + minPrio;
304 pthread_setschedparam(_ThreadHandle, SCHED_FIFO, &sp);
305 break;
307 default:
308 sp.sched_priority = 0;
309 pthread_setschedparam(_ThreadHandle, SCHED_OTHER, &sp);
314 * getUserName
316 std::string CPThread::getUserName()
318 struct passwd *pw = getpwuid(getuid());
320 if (!pw)
321 return "";
323 return pw->pw_name;
327 // **** Process
329 // The current process
330 CPProcess CurrentProcess;
332 // Get the current process
333 IProcess *IProcess::getCurrentProcess ()
335 return &CurrentProcess;
339 * getCPUMask
341 uint64 CPProcess::getCPUMask()
343 #ifdef __USE_GNU
345 uint64 cpuMask = 0;
346 sint res = sched_getaffinity(getpid(), sizeof(uint64), (cpu_set_t*)&cpuMask);
348 if (res)
350 nlwarning("sched_getaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno));
351 return 0;
354 return cpuMask;
356 #else // __USE_GNU
358 return 0;
360 #endif // __USE_GNU
363 /// set the CPU mask
364 bool CPProcess::setCPUMask(uint64 cpuMask)
366 #ifdef __USE_GNU
368 sint res = sched_setaffinity(getpid(), sizeof(uint64), (const cpu_set_t*)&cpuMask);
370 if (res)
372 nlwarning("sched_setaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno));
373 return false;
376 return true;
378 #else // __USE_GNU
380 return false;
382 #endif // __USE_GNU
386 } // NLMISC
388 #else // NL_OS_UNIX
390 // remove stupid VC6 warnings
391 void foo_p_thread_cpp() {}
393 #endif // NL_OS_UNIX