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 }