1 /** 
2  * Defines the `Future` type and related types
3  */
4 module guillotine.future;
5 
6 // TODO: Examine the below import which seemingly fixes stuff for libsnooze
7 import libsnooze.clib;
8 import libsnooze;
9 
10 import guillotine.result : Result;
11 
12 /** 
13  * Defines the state of the `Future`
14  */
15 public enum State
16 {
17     NOT_STARTED,
18     RUNNING,
19     FINISHED,
20     ERRORED
21 }
22 
23 /** 
24  * Defines a future
25  */
26 public final class Future
27 {
28     /** 
29      * `libsnooze` event
30      */
31     private Event event;
32 
33     /** 
34      * State of the future
35      */
36     package State state = State.NOT_STARTED;
37 
38     /** 
39      * The result of the task (on success)
40      */
41     private Result result; // TODO: Volatile maybe?
42 
43     /** 
44      * The error result (on error)
45      */
46     private Exception errResult; // TODO: Volatile maybe?
47 
48     /** 
49      * Constructs a new `Future`
50      */
51     public this()
52     {
53         this.event = new Event();
54     }
55 
56     /** 
57      * Awaits this future either returning the result
58      * from its computation throwing the exception which
59      * occurred during it.
60      *
61      * Returns: the `Result` from the computation
62      * Throws:
63      *   `GuillotineException` on fatal error
64      * Throws:
65      *   `Exception` the exception that occurred during
66      * computation
67      */
68     public Result await()
69     {
70         // If we are already finished, return the value
71         if(this.state == State.FINISHED)
72         {
73             return this.result;
74         }
75         // If we had an error then throw the error
76         else if(this.state == State.ERRORED)
77         {
78             throw errResult;
79         }
80         // If we are in RUNNING or NOT_STARTED
81         // (Note: this can happen in executor usage too
82         // ( instead of just standalone usage as the time before
83         // the FutureTask sets to RUNNING and when our main thread
84         // calls await() can have it pickup on the NOT_STARTED case)
85         else
86         {
87             bool doneYet = false;
88             while(!doneYet)
89             {
90                 try
91                 {
92                     event.wait();
93                     doneYet = true;
94                 }
95                 catch(InterruptedException e)
96                 {
97                     // Do nothing
98                 }
99                 catch(FatalException e)
100                 {
101                     // TODO: Throw a FatalGuillaotine here
102                     // TODO: make a custom guillotine exception
103                 }
104             }
105 
106             // If we had an error then throw it
107             if(this.state == State.ERRORED)
108             {
109                 throw this.errResult;
110             }
111             // If we finished successfully, then return the result
112             // (Note: That is the only way this branch would run)
113             else
114             {
115                 return this.result;
116             }
117         }
118     }
119 
120     /** 
121      * Sets this `Future` as completed by storing the
122      * provided result into it and waking up anybody
123      * who was awaiting it
124      *
125      * Params:
126      *   result = the task's result
127      *
128      * Throws:
129      *   TODO: Handle the libsnooze event
130      */
131     package void complete(Result result)
132     {
133         // Store the result
134         this.result = result;
135 
136         // Set the state
137         this.state = State.FINISHED;
138 
139         // Wake up any sleepers
140         this.event.notifyAll();
141     }
142 
143     /** 
144      * Sets this `Future` into the error state and wakes
145      * up anybody who was awaiting it
146      *
147      * Params:
148      *   errResult = the `Exception` which occurred
149      * Throws:
150      *   TODO: handle the libsnooze event
151      */
152     package void error(Exception errResult)
153     {
154         // Store the error
155         this.errResult = errResult;
156 
157         // Set the state
158         this.state = State.ERRORED;
159 
160         // Wake up any sleepers
161         this.event.notifyAll();
162     }
163 }