1 module served.utils.fibermanager;
2 
3 // debug = Fibers;
4 
5 import core.thread;
6 
7 import std.algorithm;
8 import std.experimental.logger;
9 import std.range;
10 
11 import served.utils.memory;
12 
13 public import core.thread : Fiber, Thread;
14 
15 struct FiberManager
16 {
17 	private Fiber[] fibers;
18 
19 	void call()
20 	{
21 		size_t[] toRemove;
22 		foreach (i, fiber; fibers)
23 		{
24 			if (fiber.state == Fiber.State.TERM)
25 				toRemove ~= i;
26 			else
27 				fiber.call();
28 		}
29 		foreach_reverse (i; toRemove)
30 		{
31 			debug (Fibers)
32 				tracef("Releasing fiber %s", cast(void*) fibers[i]);
33 			destroyUnset(fibers[i]);
34 			fibers = fibers.remove(i);
35 		}
36 	}
37 
38 	size_t length() const @property
39 	{
40 		return fibers.length;
41 	}
42 
43 	/// Makes a fiber call alongside other fibers with this manager. This transfers the full memory ownership to the manager.
44 	/// Fibers should no longer be accessed when terminating.
45 	void put(Fiber fiber, string file = __FILE__, int line = __LINE__)
46 	{
47 		debug (Fibers)
48 			tracef("Putting fiber %s in %s:%s", cast(void*) fiber, file, line);
49 		fibers.assumeSafeAppend ~= fiber;
50 	}
51 
52 	/// ditto
53 	void opOpAssign(string op : "~")(Fiber fiber, string file = __FILE__, int line = __LINE__)
54 	{
55 		put(fiber, file, line);
56 	}
57 }
58 
59 private template hasInputRanges(Args...)
60 {
61 	static if (Args.length == 0)
62 		enum hasInputRanges = false;
63 	else static if (isInputRange!(Args[$ - 1]))
64 		enum hasInputRanges = true;
65 	else
66 		enum hasInputRanges = hasInputRanges!(Args[0 .. $ - 1]);
67 }
68 
69 // ridiculously high fiber size (192 KiB per fiber to create), but for parsing big files this is needed to not segfault in libdparse
70 void joinAll(size_t fiberSize = 4096 * 48, Fibers...)(Fibers fibers)
71 {
72 	FiberManager f;
73 	enum anyInputRanges = hasInputRanges!Fibers;
74 	static if (anyInputRanges)
75 	{
76 		Fiber[] converted;
77 		converted.reserve(Fibers.length);
78 		void addFiber(Fiber fiber)
79 		{
80 			converted ~= fiber;
81 		}
82 	}
83 	else
84 	{
85 		int i;
86 		Fiber[Fibers.length] converted;
87 
88 		void addFiber(Fiber fiber)
89 		{
90 			converted[i++] = fiber;
91 		}
92 	}
93 
94 	foreach (fiber; fibers)
95 	{
96 		static if (isInputRange!(typeof(fiber)))
97 		{
98 			foreach (fib; fiber)
99 			{
100 				static if (is(typeof(fib) : Fiber))
101 					addFiber(fib);
102 				else static if (__traits(hasMember, fib, "toFiber"))
103 					addFiber(fib.toFiber);
104 				else static if (__traits(hasMember, fib, "getYield"))
105 					addFiber(new Fiber(&fib.getYield, fiberSize));
106 				else
107 					addFiber(new Fiber(fib, fiberSize));
108 			}
109 		}
110 		else
111 		{
112 			static if (is(typeof(fiber) : Fiber))
113 				addFiber(fiber);
114 			else static if (__traits(hasMember, fiber, "toFiber"))
115 				addFiber(fiber.toFiber);
116 			else static if (__traits(hasMember, fiber, "getYield"))
117 				addFiber(new Fiber(&fiber.getYield, fiberSize));
118 			else
119 				addFiber(new Fiber(fiber, fiberSize));
120 		}
121 	}
122 	f.fibers = converted[];
123 	while (f.length)
124 	{
125 		f.call();
126 		Fiber.yield();
127 	}
128 }