1 /// Assorted helpers
2 module mecca.reactor.utils;
3 
4 // Licensed under the Boost license. Full copyright information in the AUTHORS file
5 
6 import std.range;
7 
8 import mecca.log;
9 import mecca.reactor;
10 
11 /**
12  * Pointer to data on another Fiber's stack
13  *
14  * This is a pointer to data that reside on another fiber's stack. It is assumed that the other fiber knows not
15  * to exit the function while the pointer is in effect.
16  *
17  * This construct protects against the case that the other fiber is killed while the pointer is still live.
18  *
19  * Params:
20  * T = The pointer type to use
21  */
22 struct FiberPointer(T) {
23 private:
24     FiberHandle fibHandle;
25     T* ptr;
26 
27 public:
28     /**
29      * Construct a FiberPointer.
30      */
31     this(T* ptr) nothrow @safe @nogc {
32         this.fibHandle = theReactor.currentFiberHandle();
33         this.ptr = ptr;
34     }
35 
36     /// Reports whether the pointer is currently valid
37     @property bool isValid() const nothrow @safe @nogc {
38         return fibHandle.isValid;
39     }
40 
41     /**
42      * Returns the pointer.
43      *
44      * Returns:
45      * Returns the pointer or `null` if the fiber quit
46      */
47     @property T* get() nothrow @safe @nogc {
48         return fibHandle.isValid ? ptr : null;
49     }
50 
51     /// Return a handle to the owning fiber, if valid
52     @property
53     auto ownerFiber() const nothrow @safe @nogc {
54         assert(this.isValid);
55         return this.fibHandle;
56     }
57 
58     /// Reset the pointer
59     @notrace void reset() nothrow @safe @nogc {
60         this = FiberPointer.init;
61     }
62 
63     /// The `FiberPointer` acts as the pointer itself
64     alias get this;
65 }
66 
67 /// Create a FiberPointer from a pointer
68 auto makeFiberPointer(T)(T* ptr) {
69     return FiberPointer!T(ptr);
70 }
71 
72 /**
73   "Play nice" input range filter
74 
75   Injecting this into an input range processing chain will make sure that an occasional context switch happens during
76   long processing.
77 
78   Each call to `popFront` may result in a context switch
79  */
80 auto contextSwitchingRange(R)(R range) if( isInputRange!R ) {
81     static struct ContextSwitchingRange(R) {
82         R range;
83         alias range this;
84         ref auto popFront() {
85             theReactor.considerYield();
86             return range.popFront;
87         }
88     }
89     return ContextSwitchingRange!R(range);
90 }