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 #include <pthread.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <limits.h>
29 #include <stdlib.h>
30 
31 #ifdef __APPLE_CC__
32 #include <thread>
33 #endif
34 
35 #define _DARWIN_C_SOURCE
36 #include <sys/mman.h>
37 
38 #ifndef MAP_ANONYMOUS
39     #define MAP_ANONYMOUS MAP_ANON
40 #endif
41 
42 #ifndef MAP_STACK
43     #define MAP_STACK (0)
44 #endif
45 
46 #include <Platform/Common/MTThread.h>
47 
48 namespace MT
49 {
50 	class _Fiber;
51 
52 	class Thread : public ThreadBase
53 	{
54 		pthread_t thread;
55 		pthread_attr_t threadAttr;
56 
57     char* stackRawMemory;
58     char* stackBottom;
59     size_t stackRawMemorySize;
60     size_t stackSize;
61 
62 		bool isStarted;
63 
64 		static void* ThreadFuncInternal(void *pThread)
65 		{
66 			Thread * self = (Thread *)pThread;
67 
68 			self->func(self->funcData);
69 
70 			return nullptr;
71 		}
72 
73 	public:
74 
75 		Thread()
76 			: stackRawMemory(nullptr)
77             , stackBottom(nullptr)
78 			, stackRawMemorySize(0)
79 			, isStarted(false)
80 		{
81 		}
82 
83 		void* GetStackBottom()
84 		{
85 			return stackBottom;
86 		}
87 
88 		size_t GetStackSize()
89 		{
90 			return stackSize;
91 		}
92 
93 
94 		void Start(size_t _stackSize, TThreadEntryPoint entryPoint, void *userData)
95 		{
96 			MT_ASSERT(!isStarted, "Thread already stared");
97 
98 			MT_ASSERT(func == nullptr, "Thread already started");
99 
100 			func = entryPoint;
101 			funcData = userData;
102 
103 
104             int pageSize = sysconf(_SC_PAGE_SIZE);
105             int pagesCount = _stackSize / pageSize;
106 
107             //need additional page for stack tail
108             if ((_stackSize % pageSize) > 0)
109             {
110                 pagesCount++;
111             }
112 
113             //protected guard page
114             pagesCount++;
115 
116             stackRawMemorySize = pagesCount * pageSize;
117 
118             stackRawMemory = (char*)mmap(NULL, stackRawMemorySize, PROT_READ | PROT_WRITE,  MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
119 
120             MT_ASSERT((void *)stackRawMemory != (void *)-1, "Can't allocate memory");
121 
122             stackBottom = stackRawMemory + pageSize;
123             //char* stackTop = stackRawMemory + stackMemorySize;
124 
125             int res = mprotect(stackRawMemory, pageSize, PROT_NONE);
126             MT_ASSERT(res == 0, "Can't protect memory");
127 
128             stackSize = stackRawMemorySize - pageSize;
129 
130 			MT_ASSERT(stackSize >= PTHREAD_STACK_MIN, "Thread stack to small");
131 
132 
133 			int err = pthread_attr_init(&threadAttr);
134 			MT_ASSERT(err == 0, "pthread_attr_init - error");
135 
136 			err = pthread_attr_setstack(&threadAttr, stackBottom, stackSize);
137 			MT_ASSERT(err == 0, "pthread_attr_setstack - error");
138 
139 			err = pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_JOINABLE);
140 			MT_ASSERT(err == 0, "pthread_attr_setdetachstate - error");
141 
142 			isStarted = true;
143 
144 			err = pthread_create(&thread, &threadAttr, ThreadFuncInternal, this);
145 			MT_ASSERT(err == 0, "pthread_create - error");
146 		}
147 
148 		void Stop()
149 		{
150 			MT_ASSERT(isStarted, "Thread is not started");
151 
152 			if (func == nullptr)
153 			{
154 				return;
155 			}
156 
157 			void *threadStatus = nullptr;
158 			int err = pthread_join(thread, &threadStatus);
159 			MT_ASSERT(err == 0, "pthread_join - error");
160 
161 			err = pthread_attr_destroy(&threadAttr);
162 			MT_ASSERT(err == 0, "pthread_attr_destroy - error");
163 
164 			func = nullptr;
165 			funcData = nullptr;
166 
167 			if (stackRawMemory)
168 			{
169                 int res = munmap(stackRawMemory, stackRawMemorySize);
170                 MT_ASSERT(res == 0, "Can't free memory");
171 				stackRawMemory = nullptr;
172 			}
173 			stackSize = 0;
174 
175 			isStarted = false;
176 		}
177 
178 		bool IsCurrentThread() const
179 		{
180 			if(!isStarted)
181 			{
182 				return false;
183 			}
184 
185 			pthread_t callThread = pthread_self();
186 			if (pthread_equal(callThread, thread))
187 			{
188 					return true;
189 			}
190 			return false;
191 		}
192 
193 		static int GetNumberOfHardwareThreads()
194 		{
195 #ifdef __APPLE_CC__
196             return std::thread::hardware_concurrency();
197 #else
198 			long numberOfProcessors = sysconf( _SC_NPROCESSORS_ONLN );
199 			return (int)numberOfProcessors;
200 #endif
201 		}
202 
203 		static void Sleep(uint32 milliseconds)
204 		{
205       struct timespec req;
206       time_t sec = (int)(milliseconds/1000);
207       milliseconds = milliseconds - (sec*1000);
208       req.tv_sec = sec;
209       req.tv_nsec = milliseconds * 1000000L;
210       while (nanosleep(&req,&req) == -1 )
211 			{
212 				continue;
213 			}
214 		}
215 
216 	};
217 
218 
219 }
220 
221 
222