1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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>
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/>.
24 #include "nel/misc/types_nl.h"
25 #include "nel/misc/debug.h"
29 #include "nel/misc/p_thread.h"
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.");
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
);
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
;
109 throw EThread("Thread ended after being detached, this should not happen");
122 CPThread::CPThread(IRunnable
*runnable
, uint32 stackSize
)
123 : Runnable(runnable
),
124 _State(ThreadStateNone
),
125 _StackSize(stackSize
)
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
143 void CPThread::start()
145 pthread_attr_t tattr
;
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
;
195 void CPThread::terminate()
197 if (_State
== ThreadStateRunning
)
199 // cancel only if started
200 pthread_cancel(_ThreadHandle
);
201 _State
= ThreadStateFinished
; // set to finished
208 void CPThread::wait ()
210 if (_State
== ThreadStateRunning
)
212 int error
= pthread_join(_ThreadHandle
, 0);
218 throw EThread("Thread is not joinable");
220 throw EThread("No thread found with this id");
222 throw EThread("Deadlock detected or calling thread waits for itself");
224 throw EThread("Unknown thread join error");
226 if(_State
!= ThreadStateFinished
)
227 throw EThread("Thread did not finish, this should not happen");
234 bool CPThread::setCPUMask(uint64 cpuMask
)
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
);
244 nlwarning("pthread_setaffinity_np() returned %d", res
);
260 uint64
CPThread::getCPUMask()
264 nlwarning("This code does not work. May cause a segmentation fault...");
268 sint res
= pthread_getaffinity_np(_ThreadHandle
, sizeof(uint64
), (cpu_set_t
*)&cpuMask
);
272 nlwarning("pthread_getaffinity_np() returned %d", res
);
285 void CPThread::setPriority(TThreadPriority 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
);
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
);
308 sp
.sched_priority
= 0;
309 pthread_setschedparam(_ThreadHandle
, SCHED_OTHER
, &sp
);
316 std::string
CPThread::getUserName()
318 struct passwd
*pw
= getpwuid(getuid());
329 // The current process
330 CPProcess CurrentProcess
;
332 // Get the current process
333 IProcess
*IProcess::getCurrentProcess ()
335 return &CurrentProcess
;
341 uint64
CPProcess::getCPUMask()
346 sint res
= sched_getaffinity(getpid(), sizeof(uint64
), (cpu_set_t
*)&cpuMask
);
350 nlwarning("sched_getaffinity() returned %d, errno = %d: %s", res
, errno
, strerror(errno
));
364 bool CPProcess::setCPUMask(uint64 cpuMask
)
368 sint res
= sched_setaffinity(getpid(), sizeof(uint64
), (const cpu_set_t
*)&cpuMask
);
372 nlwarning("sched_setaffinity() returned %d, errno = %d: %s", res
, errno
, strerror(errno
));
390 // remove stupid VC6 warnings
391 void foo_p_thread_cpp() {}