1 // The MIT License (MIT)
2 //
3 // 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
4 //
5 // 	Permission is hereby granted, free of charge, to any person obtaining a copy
6 // 	of this software and associated documentation files (the "Software"), to deal
7 // 	in the Software without restriction, including without limitation the rights
8 // 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // 	copies of the Software, and to permit persons to whom the Software is
10 // 	furnished to do so, subject to the following conditions:
11 //
12 //  The above copyright notice and this permission notice shall be included in
13 // 	all copies or substantial portions of the Software.
14 //
15 // 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // 	THE SOFTWARE.
22 
23 #pragma once
24 
25 #ifndef __MT_THREAD__
26 #define __MT_THREAD__
27 
28 
29 #include <pthread.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <limits.h>
33 #include <stdlib.h>
34 
35 #ifdef __APPLE_CC__
36 #include <thread>
37 #endif
38 
39 #define _DARWIN_C_SOURCE
40 #include <sys/mman.h>
41 
42 #ifndef MAP_ANONYMOUS
43     #define MAP_ANONYMOUS MAP_ANON
44 #endif
45 
46 #ifndef MAP_STACK
47     #define MAP_STACK (0)
48 #endif
49 
50 #include <Platform/Common/MTThread.h>
51 #include <MTAllocator.h>
52 
53 namespace MT
54 {
55 	class _Fiber;
56 
57 	class Thread : public ThreadBase
58 	{
59 		pthread_t thread;
60 		pthread_attr_t threadAttr;
61 
62 		Memory::StackDesc stackDesc;
63 
64     size_t stackSize;
65 
66 		bool isStarted;
67 
68 		static void* ThreadFuncInternal(void *pThread)
69 		{
70 			Thread * self = (Thread *)pThread;
71 
72 			self->func(self->funcData);
73 
74 			return nullptr;
75 		}
76 
77 	public:
78 
79 		Thread()
80 			: stackSize(0)
81 			, isStarted(false)
82 		{
83 		}
84 
85 		void* GetStackBottom()
86 		{
87 			return stackDesc.stackBottom;
88 		}
89 
90 		size_t GetStackSize()
91 		{
92 			return stackSize;
93 		}
94 
95 
96 		void Start(size_t _stackSize, TThreadEntryPoint entryPoint, void *userData)
97 		{
98 			MT_ASSERT(!isStarted, "Thread already stared");
99 
100 			MT_ASSERT(func == nullptr, "Thread already started");
101 
102 			func = entryPoint;
103 			funcData = userData;
104 
105 			stackDesc = Memory::AllocStack(_stackSize);
106 			stackSize = stackDesc.GetStackSize();
107 
108 			MT_ASSERT(stackSize >= PTHREAD_STACK_MIN, "Thread stack to small");
109 
110 			int err = pthread_attr_init(&threadAttr);
111 			MT_ASSERT(err == 0, "pthread_attr_init - error");
112 
113 			err = pthread_attr_setstack(&threadAttr, stackDesc.stackBottom, stackSize);
114 			MT_ASSERT(err == 0, "pthread_attr_setstack - error");
115 
116 			err = pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_JOINABLE);
117 			MT_ASSERT(err == 0, "pthread_attr_setdetachstate - error");
118 
119 			isStarted = true;
120 
121 			err = pthread_create(&thread, &threadAttr, ThreadFuncInternal, this);
122 			MT_ASSERT(err == 0, "pthread_create - error");
123 		}
124 
125 		void Stop()
126 		{
127 			MT_ASSERT(isStarted, "Thread is not started");
128 
129 			if (func == nullptr)
130 			{
131 				return;
132 			}
133 
134 			void *threadStatus = nullptr;
135 			int err = pthread_join(thread, &threadStatus);
136 			MT_ASSERT(err == 0, "pthread_join - error");
137 
138 			err = pthread_attr_destroy(&threadAttr);
139 			MT_ASSERT(err == 0, "pthread_attr_destroy - error");
140 
141 			func = nullptr;
142 			funcData = nullptr;
143 
144 			if (stackDesc.stackMemory != nullptr)
145 			{
146 				Memory::FreeStack(stackDesc);
147 			}
148 
149 			stackSize = 0;
150 			isStarted = false;
151 		}
152 
153 		bool IsCurrentThread() const
154 		{
155 			if(!isStarted)
156 			{
157 				return false;
158 			}
159 
160 			pthread_t callThread = pthread_self();
161 			if (pthread_equal(callThread, thread))
162 			{
163 					return true;
164 			}
165 			return false;
166 		}
167 
168 		static int GetNumberOfHardwareThreads()
169 		{
170 #ifdef __APPLE_CC__
171             return std::thread::hardware_concurrency();
172 #else
173 			long numberOfProcessors = sysconf( _SC_NPROCESSORS_ONLN );
174 			return (int)numberOfProcessors;
175 #endif
176 		}
177 
178 		static void Sleep(uint32 milliseconds)
179 		{
180       struct timespec req;
181       time_t sec = (int)(milliseconds/1000);
182       milliseconds = milliseconds - (sec*1000);
183       req.tv_sec = sec;
184       req.tv_nsec = milliseconds * 1000000L;
185       while (nanosleep(&req,&req) == -1 )
186 			{
187 				continue;
188 			}
189 		}
190 
191 	};
192 
193 
194 }
195 
196 
197 #endif