1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.concurrency.FuturePromise; 13 14 import hunt.concurrency.Future; 15 import hunt.concurrency.Promise; 16 17 import hunt.Exceptions; 18 import hunt.logging.ConsoleLogger; 19 20 import core.atomic; 21 import core.sync.condition; 22 import core.sync.mutex; 23 import core.thread; 24 import std.format; 25 import std.datetime; 26 27 /** 28 * 29 */ 30 class FuturePromise(T) : Future!T, Promise!T { 31 private __gshared Exception COMPLETED; 32 private shared bool _done = false; 33 private bool _isResultAvaliable = false; 34 private Exception _cause; 35 private string _id; 36 shared static this() { 37 COMPLETED = new Exception(""); 38 } 39 40 this() { 41 } 42 43 string id() { 44 return _id; 45 } 46 47 void id(string id) { 48 _id = id; 49 } 50 51 static if(is(T == void)) { 52 53 /** 54 * TODO: 55 * 1) keep this operation atomic 56 * 2) return a flag to indicate whether this option is successful. 57 */ 58 void succeeded() { 59 if (cas(&_done, false, true)) { 60 _cause = COMPLETED; 61 _isResultAvaliable = true; 62 } else { 63 warning("This promise has been done, and can't be set again."); 64 } 65 } 66 67 } else { 68 69 /** 70 * TODO: 71 * 1) keep this operation atomic 72 * 2) return a flag to indicate whether this option is successful. 73 */ 74 void succeeded(T result) { 75 if (cas(&_done, false, true)) { 76 _result = result; 77 _cause = COMPLETED; 78 _isResultAvaliable = true; 79 } else { 80 warning("This promise has been done, and can't be set again."); 81 } 82 } 83 private T _result; 84 } 85 86 /** 87 * TODO: 88 * 1) keep this operation atomic 89 * 2) return a flag to indicate whether this option is successful. 90 */ 91 void failed(Exception cause) { 92 if (cas(&_done, false, true)) { 93 _cause = cause; 94 _isResultAvaliable = true; 95 } else { 96 warning("This promise has been done, and can't be set again."); 97 } 98 } 99 100 bool cancel(bool mayInterruptIfRunning) { 101 if (cas(&_done, false, true)) { 102 static if(!is(T == void)) { 103 _result = T.init; 104 } 105 _cause = new CancellationException(""); 106 _isResultAvaliable = true; 107 // _doneCondition.notifyAll(); 108 return true; 109 } 110 return false; 111 } 112 113 bool isCancelled() { 114 if (_done) { 115 try { 116 // _latch.await(); 117 // TODO: Tasks pending completion -@zhangxueping at 2019-12-26T15:18:42+08:00 118 // 119 } catch (InterruptedException e) { 120 throw new RuntimeException(e.msg); 121 } 122 return typeid(_cause) == typeid(CancellationException); 123 } 124 return false; 125 } 126 127 bool isDone() { 128 return _done; 129 } 130 131 T get() { 132 // waitting for the result 133 version (HUNT_DEBUG) info("Waiting for a promise..."); 134 while(!_isResultAvaliable) { 135 Thread.yield(); 136 version(HUNT_DANGER_DEBUG) trace("Waiting for a promise"); 137 } 138 139 version (HUNT_DEBUG) info("Got a promise"); 140 assert(_cause !is null); 141 142 if (_cause is COMPLETED) { 143 static if(is(T == void)) { 144 return; 145 } else { 146 return _result; 147 } 148 } 149 150 CancellationException c = cast(CancellationException) _cause; 151 if (c !is null) { 152 version(HUNT_DEBUG) info("A promise cancelled."); 153 throw c; 154 } 155 156 debug warning("Get a exception in a promise: ", _cause.msg); 157 version (HUNT_DEBUG) warning(_cause); 158 throw new ExecutionException(_cause); 159 } 160 161 T get(Duration timeout) { 162 // waitting for the result 163 if(!_isResultAvaliable) { 164 version (HUNT_DEBUG) { 165 infof("Waiting for a promise in %s...", timeout); 166 } 167 auto start = Clock.currTime; 168 while (!_isResultAvaliable && Clock.currTime < start + timeout) { 169 Thread.yield(); 170 } 171 172 if (!_isResultAvaliable) { 173 debug warningf("Timeout for a promise in %s...", timeout); 174 failed(new TimeoutException("Timeout in " ~ timeout.toString())); 175 } 176 177 version (HUNT_DEBUG) { 178 auto dur = Clock.currTime - start; 179 if(dur > 5.seconds) { 180 warningf("Got a promise in %s", dur); 181 } else { 182 // infof("Got a promise in %s", dur); 183 } 184 } 185 } 186 187 if (_cause is COMPLETED) { 188 static if(is(T == void)) { 189 return; 190 } else { 191 return _result; 192 } 193 } 194 195 TimeoutException t = cast(TimeoutException) _cause; 196 if (t !is null) 197 throw t; 198 199 CancellationException c = cast(CancellationException) _cause; 200 if (c !is null) 201 throw c; 202 203 throw new ExecutionException(_cause.msg); 204 } 205 206 override string toString() { 207 static if(is(T == void)) { 208 return format("FutureCallback@%x{%b, %b, void}", toHash(), _done, _cause is COMPLETED); 209 } else { 210 return format("FutureCallback@%x{%b, %b, %s}", toHash(), _done, _cause is COMPLETED, _result); 211 } 212 } 213 }