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_FIBER__
26 #define __MT_FIBER__
27 
28 
29 #include <ucontext.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #define _DARWIN_C_SOURCE
34 #include <sys/mman.h>
35 
36 #ifndef MAP_ANONYMOUS
37     #define MAP_ANONYMOUS MAP_ANON
38 #endif
39 
40 #ifndef MAP_STACK
41     #define MAP_STACK (0)
42 #endif
43 
44 #include <MTAllocator.h>
45 #include "MTAtomic.h"
46 
47 namespace MT
48 {
49 
50 	//
51 	//
52 	//
53 	class Fiber
54 	{
55 		void* funcData;
56 		TThreadEntryPoint func;
57 
58 		Memory::StackDesc stackDesc;
59 
60 		ucontext_t fiberContext;
61 		bool isInitialized;
62 
63 		static void FiberFuncInternal(void* pFiber)
64 		{
65 			MT_ASSERT(pFiber != nullptr, "Invalid fiber");
66 			Fiber* self = (Fiber*)pFiber;
67 
68 			MT_ASSERT(self->isInitialized == true, "Using non initialized fiber");
69 
70 			MT_ASSERT(self->func != nullptr, "Invalid fiber func");
71 			self->func(self->funcData);
72 		}
73 
74 	public:
75 
76 		MT_NOCOPYABLE(Fiber);
77 
78 		Fiber()
79 			: funcData(nullptr)
80 			, func(nullptr)
81 			, isInitialized(false)
82 		{
83 			memset(&fiberContext, 0, sizeof(ucontext_t));
84 		}
85 
86 		~Fiber()
87 		{
88 			if (isInitialized)
89 			{
90 				// if func != null than we have stack memory ownership
91 				if (func != nullptr)
92 				{
93 					Memory::FreeStack(stackDesc);
94 				}
95 
96 				isInitialized = false;
97 			}
98 		}
99 
100 
101 		void CreateFromThread(Thread & thread)
102 		{
103 			MT_ASSERT(!isInitialized, "Already initialized");
104 			MT_ASSERT(thread.IsCurrentThread(), "ERROR: Can create fiber only from current thread!");
105 
106 			int res = getcontext(&fiberContext);
107 			MT_USED_IN_ASSERT(res);
108 			MT_ASSERT(res == 0, "getcontext - failed");
109 
110 			fiberContext.uc_link = nullptr;
111 			fiberContext.uc_stack.ss_sp = thread.GetStackBottom();
112 			fiberContext.uc_stack.ss_size = thread.GetStackSize();
113 			fiberContext.uc_stack.ss_flags = 0;
114 
115 			func = nullptr;
116 			funcData = nullptr;
117 
118 			isInitialized = true;
119 		}
120 
121 
122 		void Create(size_t stackSize, TThreadEntryPoint entryPoint, void *userData)
123 		{
124 			MT_ASSERT(!isInitialized, "Already initialized");
125 			MT_ASSERT(stackSize >= PTHREAD_STACK_MIN, "Stack to small");
126 
127 			func = entryPoint;
128 			funcData = userData;
129 
130 			int res = getcontext(&fiberContext);
131 			MT_USED_IN_ASSERT(res);
132 			MT_ASSERT(res == 0, "getcontext - failed");
133 
134 			stackDesc = Memory::AllocStack(stackSize);
135 
136 			fiberContext.uc_link = nullptr;
137 			fiberContext.uc_stack.ss_sp = stackDesc.stackBottom;
138 			fiberContext.uc_stack.ss_size = stackDesc.GetStackSize();
139 			fiberContext.uc_stack.ss_flags = 0;
140 
141 			makecontext(&fiberContext, (void(*)())&FiberFuncInternal, 1, (void *)this);
142 
143 			isInitialized = true;
144 		}
145 
146 		static void SwitchTo(Fiber & from, Fiber & to)
147 		{
148 			HardwareFullMemoryBarrier();
149 
150 			MT_ASSERT(from.isInitialized, "Invalid from fiber");
151 			MT_ASSERT(to.isInitialized, "Invalid to fiber");
152 
153 			int res = swapcontext(&from.fiberContext, &to.fiberContext);
154 			MT_USED_IN_ASSERT(res);
155 			MT_ASSERT(res == 0, "setcontext - failed");
156 
157 		}
158 
159 
160 
161 	};
162 
163 
164 }
165 
166 
167 #endif