1 module upromised.promise;
2 
3 import std.format : format;
4 
5 interface Promise_ {
6 	final protected void fatal() nothrow {
7 		import std.algorithm : each;
8 		import std.stdio : stderr;
9 		import upromised.backtrace : backtrace;
10 
11 		try {
12 			stderr.writeln("Fatal error");
13 			backtrace.each!(x => stderr.writeln(x));
14 		} catch(Exception) {
15 		}
16 	}
17 }
18 
19 void fatal(Exception e = null, string file = __FILE__, ulong line = __LINE__) nothrow {
20 	import core.stdc.stdlib : abort;
21 	import std.stdio : stderr;
22 	try {
23 		stderr.writeln("%s(%s): Fatal error".format(file, line));
24 		if (e) {
25 			stderr.writeln(e);
26 		}
27 	} catch(Exception) {
28 		abort();
29 	}
30 	abort();
31 }
32 
33 template Promisify(T) {
34 	static if (is(T : Promise_)) {
35 		alias Promisify = T;
36 	} else {
37 		alias Promisify = Promise!T;
38 	}
39 }
40 static assert(is(Promisify!int == Promise!int));
41 static assert(is(Promisify!(Promise!int) == Promise!int));
42 static assert(is(Promisify!void == Promise!void));
43 
44 auto promisify(T)(T a) nothrow {
45 	static if (is(T : Promise_)) {
46 		return a;
47 	} else {
48 		return Promise!T.resolved(a);
49 	}
50 }
51 auto promisifyCall(T, U...)(T a, U b) nothrow {
52 	static if (is(typeof(a(b)) == void)) {
53 		try {
54 			a(b);
55 			return Promise!void.resolved();
56 		} catch(Exception e) {
57 			return Promise!void.rejected(e);
58 		}
59 	} else {
60 		try {
61 			return promisify(a(b));
62 		} catch(Exception e) {
63 			return typeof(promisify(a(b))).rejected(e);
64 		}
65 	}
66 }
67 
68 interface Promise(T_) : Promise_ {
69 	import std.typecons : Tuple;
70 
71 	alias T = T_;
72 
73 	static if (is(T == void)) {
74 		alias Types = Tuple!();
75 		template Then(U) {
76 			alias Then = U delegate();
77 		}
78 	} else {
79 		alias Types = Tuple!(T);
80 		template Then(U) {
81 			alias Then = U delegate(T);
82 		}
83 	}
84 
85 	struct Value {
86 		this(Exception e) {
87 			this.e = e;
88 		}
89 
90 		static if (!is(T == void)) {
91 			this(Exception e, T value) {
92 				this.e = e;
93 				this.value[0] = value;
94 			}
95 		}
96 
97 		Exception e;
98 		Types value;
99 	}
100 	
101 	static if (is(T == void)) {
102 		Promisify!U then(U)(U delegate() cb) nothrow {
103 			return then2!U(cb);
104 		}
105 	} else {
106 		Promisify!U then(U)(U delegate(T) cb) nothrow {
107 			return then2!U(cb);
108 		}
109 
110 		Promisify!U then(U)(U delegate() cb) nothrow {
111 			return then2!U((_) => cb());
112 		}
113 	}
114 
115 	protected Promisify!U then2(U)(Then!U cb) nothrow {
116 		auto r = new DelegatePromise!(Promisify!U.T);
117 		thenWithTrace((value) nothrow {
118 			if (value.e !is null) {
119 				r.reject(value.e);
120 			} else {
121 				scope(failure) r.fatal();
122 				promisifyCall(cb, value.value.expand).thenWithTrace((v) nothrow {
123 					r.resolve(v);
124 				});
125 			}
126 		});
127 		return r;
128 	}
129 
130 	Promise!void except(E,U)(U delegate(E e) cb) nothrow
131 	if (is(Promisify!U.T == void) && is (E : Exception))
132 	{
133 		auto r = new DelegatePromise!void;
134 		thenWithTrace((value) nothrow {
135 			scope(failure) r.fatal();
136 			if (value.e !is null) {
137 				E e = cast(E)value.e;
138 				if (e !is null) {
139 					promisifyCall(cb, e).thenWithTrace((value) nothrow {
140 						r.resolve(value);
141 					});
142 				} else {
143 					r.reject(value.e);
144 				}
145 			} else {
146 				r.resolve();
147 			}
148 		});
149 		return r;
150 	}
151 
152 	auto finall(U2)(U2 delegate() cb) nothrow {
153 		static if (is(Promisify!U2.T == void)) {
154 			alias U = T;
155 		} else {
156 			alias U = U2;
157 		}
158 		auto r = new DelegatePromise!(Promisify!U.T);
159 		thenWithTrace((value) nothrow {
160 			scope(failure) r.fatal();
161 			promisifyCall(cb).thenWithTrace((value2) nothrow {
162 				Promisify!U.Value value3;
163 				value3.e = value2.e is null ? value.e : value2.e;
164 				static if (is(Promisify!U2.T == void)) {
165 					static if (!is(Promisify!U.T == void)) {
166 						value3.value = value.value;
167 					}
168 				} else {
169 					value3.value = value2.value;
170 				}
171 
172 				r.resolve(value3);
173 			});
174 		});
175 		return r;
176 	}
177 
178 	Promise!T failure(U)(U delegate(Exception) cb) nothrow
179 	if (is(Promisify!U.T == void))
180 	{
181 		auto r = new DelegatePromise!T;
182 		thenWithTrace((value) nothrow {
183 			if (value.e !is null) {
184 				promisifyCall(cb, value.e).thenWithTrace((value2) nothrow {
185 					r.resolve(value);
186 				});
187 			} else {
188 				r.resolve(value);
189 			}
190 		});
191 		return r;
192 	}
193 
194 	static if (is(T == void)) {
195 		static Promise!T resolved() nothrow {
196 			return resolved_(Value(null));
197 		}
198 	} else {
199 		static Promise!T resolved(T t) nothrow {
200 			return resolved_(Value(null, t));
201 		}
202 	}
203 	protected static Promise!T resolved_(Value value) {
204 		return new class Promise!T {
205 			override void then_(void delegate(Value) nothrow cb) nothrow {
206 				cb(value);
207 			}
208 		};
209 	}
210 
211 	static Promise!T rejected(Exception e) nothrow {
212 		import core.runtime : Runtime;
213 		if (e.info is null) {
214 			try {
215 				e.info = Runtime.traceHandler()(null);
216 			} catch(Exception) {
217 			}
218 		}
219 
220 		return new class Promise!T {
221 			override void then_(void delegate(Value) nothrow cb) nothrow {
222 				cb(Value(e));
223 			}
224 		};
225 	}
226 
227 	final Promise!void nothrow_() nothrow {
228 		return except((Exception e) => .fatal(e));
229 	}
230 
231 	protected void then_(void delegate(Value) nothrow cb) nothrow;
232 
233 	final public void thenWithTrace(void delegate(Value) nothrow cb) nothrow {
234 		import upromised.backtrace : backtrace, traceinfo, concat, setBasestack, recoverBasestack;
235 
236 		Throwable.TraceInfo backBt = ["*async*"].traceinfo.concat(backtrace());
237 		then_((value) nothrow {
238 			auto prev = setBasestack(backBt);
239 			scope(exit) recoverBasestack(prev);
240 			cb(value);
241 		});
242 	}
243 }
244 
245 class DelegatePromise(T) : Promise!T {
246 	bool resolved;
247 	Value result;
248 	void delegate(Value) nothrow[] pending;
249 
250 	override void then_(void delegate(Value) nothrow cb) nothrow {
251 		if (resolved) {
252 			cb(result);
253 		} else {
254 			pending ~= cb;
255 		}
256 	}
257 
258 	void resolve(Value value) nothrow {
259 		scope(failure) fatal();
260 		assert(!resolved);
261 		result = value;
262 		resolved = true;
263 		foreach(cb; pending) {
264 			cb(result);
265 		}
266 		pending = null;
267 	}
268 
269 	static if (is(T == void)) {
270 		void resolve() nothrow {
271 			resolve(Value());
272 		}
273 	} else {
274 		void resolve(T value) nothrow {
275 			resolve(Value(null, value));
276 		}
277 	}
278 	void reject(Exception e) nothrow {
279 		resolve(Value(e));
280 	}
281 }
282 unittest { // Multiple then
283     auto a = new DelegatePromise!int;
284     int sum = 0;
285     a.then((int a) { sum += a; });
286     a.then((a) { sum += a; });
287     a.resolve(2);
288     assert(sum == 4);
289     a.then((a) { sum += a; });
290     assert(sum == 6);
291 }
292 unittest { // Void promise
293     auto a = new DelegatePromise!void;
294     bool called = false;
295     a.then(() {
296         called = true;
297     }).nothrow_();
298     assert(!called);
299     a.resolve();
300     assert(called);
301 }
302 
303 unittest { // Void Chaining
304     auto a = new DelegatePromise!int;
305     int sum = 0;
306     a.then((a) {
307         sum += a;
308     }).then(() {
309         sum += 3;
310     }).nothrow_();
311     assert(sum == 0);
312     a.resolve(3);
313     assert(sum == 6);
314 }
315 unittest { // Chaining
316     auto a = new DelegatePromise!int;
317     int delayValue;
318     DelegatePromise!string delayed;
319     string finalValue;
320     
321     a.then((a) {
322         return a * 2;
323     }).then((a) {
324         delayed = new DelegatePromise!string;
325         delayValue = a;
326         return delayed;
327     }).then((a) {
328         finalValue = a;
329     }).nothrow_();
330     a.resolve(1);
331     assert(delayValue == 2);
332     assert(finalValue == "");
333     delayed.resolve("2");
334     assert(finalValue == "2");
335 }
336 unittest { //Exceptions
337     auto a = new DelegatePromise!int;
338 
339     auto err = new Exception("yada");
340     bool caught = false;
341     a.except((Exception e) {
342         assert(err is e);
343         caught = true;
344     }).nothrow_();
345     assert(!caught);
346     a.reject(err);
347     assert(caught);
348 }
349 unittest { //Exception chaining
350     auto a = new DelegatePromise!int;
351     class X : Exception {
352         this() {
353             super("X");
354         }
355     }
356 
357     bool caught = false;
358     auto err = new Exception("yada");
359     a.except((X _) {
360         assert(false);
361     }).except((Exception e) {
362         assert(e is err);
363         caught = true;
364     }).nothrow_();
365     assert(!caught);
366     a.reject(err);
367     assert(caught);
368 }
369 unittest {
370     auto a = new DelegatePromise!int;
371     auto err = new Exception("yada");
372     bool caught = false;
373     a.then((a) {
374         throw err;
375     }).then(() {
376         assert(false);
377     }).except((Exception e) {
378         assert(e == err);
379         caught = true;
380     }).nothrow_();
381     assert(!caught);
382     a.resolve(2);
383     assert(caught);
384 }
385 // Finall propagates the other value
386 unittest {
387 	bool called;
388 	Promise!void.resolved()
389 	.then(() => 2)
390 	.finall(() => 3)
391 	.then((a) {
392 		assert(a == 3);
393 		return a;
394 	}).finall(() {
395 	}).then((a) {
396 		assert(a == 3);
397 		called = true;
398 	}).nothrow_();
399 	assert(called);
400 }
401 // Failure method is not called on success
402 unittest {
403 	bool called;
404 	Promise!int.resolved(3)
405 	.failure((Exception _) {
406 		assert(false);
407 	}).then((a) {
408 		assert(a == 3);
409 		called = true;
410 	}).nothrow_();
411 	assert(called);
412 }
413 // Failure method is called on error
414 unittest {
415 	bool called;
416 	bool called2;
417 	auto err = new Exception("err");
418 	Promise!int.rejected(err)
419 	.failure((Exception e) {
420 		assert(e is err);
421 		called = true;
422 	}).except((Exception e) {
423 		assert(e is err);
424 		called2 = true;
425 	}).nothrow_();
426 	assert(called);
427 	assert(called2);
428 }
429 // Failure might be delayed
430 unittest {
431 	DelegatePromise!void delay;
432 	bool called2;
433 	auto err = new Exception("err");
434 	Promise!int.rejected(err)
435 	.failure((Exception e) {
436 		assert(e is err);
437 		delay = new DelegatePromise!void;
438 		return delay;
439 	}).except((Exception e) {
440 		assert(e is err);
441 		called2 = true;
442 	}).nothrow_();
443 	assert(delay !is null);
444 	assert(!called2);
445 	delay.resolve();
446 	assert(called2);
447 }
448 interface PromiseIterator(T) {
449 	struct ItValue {
450 		bool eof;
451 		T value;
452 	}
453 	Promise!ItValue next(Promise!bool done = break_);
454 
455 	final Promise!bool each(U)(U delegate(T) cb) nothrow
456 	if (is(Promisify!U.T == void) || is(Promisify!U.T == bool))
457 	{
458 		import upromised.backtrace : backtrace, traceinfo, concat, setBasestack, recoverBasestack;
459 
460 		Throwable.TraceInfo backBt = ["*async*"].traceinfo.concat(backtrace());
461 		
462 		static if (is(Promisify!U.T == void)) {
463 			bool delegate() boolify = () => true;
464 		} else {
465 			bool delegate(bool) boolify = (bool a) => a;
466 		}
467 
468 		bool eof;
469 		return do_while(() {
470 			auto done = new DelegatePromise!bool;
471 			return promisifyCall(&next, done).then((a) {
472 				if (a.eof) {
473 					done.resolve(false);
474 					eof = true;
475 					return break_;
476 				} else {
477 					auto prev = setBasestack(backBt);
478 					scope(exit) recoverBasestack(prev);
479 					return promisifyCall(cb, a.value).then(boolify).then((cont) {
480 						done.resolve(cont);
481 						return cont;
482 					});
483 				}
484 			});
485 		}).then(() => eof);
486 	}
487 }
488 class DelegatePromiseIterator(T) : PromiseIterator!T {
489 	import std.typecons : Tuple, tuple;
490 
491 	alias Value = Promise!ItValue.Value;
492 	Tuple!(DelegatePromise!ItValue, Promise!bool) pending;
493 	Tuple!(Promise!ItValue, DelegatePromise!bool)[] buffer;
494 
495 	override Promise!ItValue next(Promise!bool done) {
496 		if (buffer.length > 0) {
497 			auto next = buffer[0];
498 			buffer = buffer[1..$];
499 			done.thenWithTrace(a => next[1].resolve(a));
500 			return next[0];
501 		} else {
502 			assert(pending[0] is null);
503 			pending = tuple(new DelegatePromise!ItValue, done);
504 			return pending[0];
505 		}
506 	}
507 
508 	Promise!bool resolve(T a) nothrow {
509 		return resolve(Value(null, ItValue(false, a)));
510 	}
511 
512 	Promise!bool resolve() nothrow {
513 		return resolve(Value(null, ItValue(true)));
514 	}
515 
516 	Promise!bool reject(Exception e) nothrow {
517 		return resolve(Value(e));
518 	}
519 
520 	Promise!bool resolve(Value a) nothrow {
521 		auto cb = getPending;
522 		if (cb[0]) {
523 			cb[0].resolve(a);
524 			return cb[1];
525 		} else {
526 			auto r = new DelegatePromise!bool;
527 			buffer ~= tuple(Promise!ItValue.resolved_(a), r);
528 			return r;
529 		}
530 	}
531 
532 	Tuple!(DelegatePromise!ItValue, Promise!bool) getPending() nothrow {
533 		auto r = pending;
534 		pending[0] = null;
535 		return r;
536 	}
537 }
538 
539 unittest { //Iterator
540     DelegatePromiseIterator!int a = new DelegatePromiseIterator!int;
541     int sum = 0;
542     a.resolve(1);
543     assert(sum == 0);
544     a.each((b) {
545         sum += b;
546         return true;
547     }).then((bool) {
548         sum = 0;
549         return;
550     }).nothrow_();
551     assert(sum == 1);
552     a.resolve(2);
553     assert(sum == 3);
554     a.resolve();
555     assert(sum == 0);
556 }
557 unittest { //Iterator catching
558     DelegatePromiseIterator!int a = new DelegatePromiseIterator!int;
559     auto err = new Exception("yada");
560     bool caught = false;
561     a.each((b) {
562         throw err;
563     }).except((Exception e) {
564         assert(e == err);
565         caught = true;
566     }).nothrow_();
567     assert(!caught);
568     a.resolve(2);
569     assert(caught);
570     caught = false;
571     a.resolve(3);
572     assert(!caught);
573     a.each((b) {
574         assert(b == 3);
575         caught = true;
576     });
577     assert(caught);
578 }
579 unittest { //Resolve done Promise
580     auto a = new DelegatePromiseIterator!int;
581     bool done = false;
582     a.resolve(2).then((a) {
583         assert(!a);
584         done = true;
585     }).nothrow_();
586     assert(!done);
587     a.each((a) {
588         return false;
589     });
590     assert(done);
591     done = false;
592     DelegatePromise!bool delayed;
593     a.each((a) {
594         delayed = new DelegatePromise!bool;
595         return delayed;
596     }).nothrow_();
597     assert(!done);
598     bool done2 = false;
599     a.resolve(3).then((a) {
600         assert(a);
601         done = true;
602     }).nothrow_();
603     a.resolve(4).then((a) {
604         assert(!a);
605         done2 = true;
606     }).nothrow_();
607     assert(!done);
608     assert(!done2);
609     delayed.resolve(true);
610     assert(done);
611     assert(!done2);
612     delayed.resolve(false);
613     assert(done);
614     assert(done2);
615 }
616 unittest { // Rejecting iterator
617     auto a = new DelegatePromiseIterator!int;
618     bool called = false;
619     auto err = new Exception("yada");
620     class X : Exception {
621         this() {
622             super("yada");
623         }
624     }
625     a.each((a) {
626         assert(false);
627     }).except((X err) {
628         assert(false);
629     }).except((Exception e) {
630         assert(err is e);
631         called = true;
632     }).nothrow_();
633     assert(!called);
634     a.reject(err);
635     assert(called);
636 }
637 unittest { //Resolved and rejected promise constructor
638     bool called = false;
639     Promise!void.resolved().then(() {
640         called = true;
641     });
642     assert(called);
643     called = false;
644     Promise!int.resolved(3).then((a) {
645         assert(a == 3);
646         called = true;
647     }).nothrow_();
648     assert(called);
649     called = false;
650     auto err = new Exception("yada");
651     Promise!int.rejected(err).except((Exception e) {
652         assert(e is err);
653         called = true;
654     }).nothrow_();
655     assert(called);
656 }
657 unittest { //Finally
658     auto a = new DelegatePromise!int;
659     auto called = [false,false,false];
660     auto err = new Exception("yada");
661     a.then((a) {
662     }).except((Exception e) {
663         assert(false);
664     }).finall(() {
665         called[0] = true;
666     }).then(() {
667         throw err;
668     }).finall(() {
669         called[1] = true;
670     }).except((Exception e) {
671         assert(called[1]);
672         called[2] = true;
673         assert(e is err);
674     }).nothrow_();
675     assert(!called[0]);
676     assert(!called[1]);
677     assert(!called[2]);
678     a.resolve(1);
679     assert(called[0]);
680     assert(called[1]);
681     assert(called[2]);
682 }
683 unittest { //Finally might throw Exception
684     auto a = new DelegatePromise!int;
685     auto called = false;
686     auto err = new Exception("yada");
687     a.finall(() {
688         throw err;
689     }).then(() {
690         assert(false);
691     }).except((Exception e) {
692         assert(e is err);
693         called = true;
694     }).nothrow_();
695     assert(!called);
696     a.resolve(2);
697     assert(called);
698 }
699 unittest { //Finally return promise
700     auto a = new DelegatePromise!int;
701     auto called = false;
702     auto err = new Exception("yada");
703     a.finall(() {
704         return Promise!void.rejected(err);
705     }).then(() {
706         assert(false);
707     }).except((Exception e) {
708         assert(err is e);
709         called = true;
710     }).nothrow_();
711     assert(!called);
712     a.resolve(2);
713     assert(called);
714 }
715 unittest { //Return failed promise
716     auto a = new DelegatePromise!int;
717     auto called = false;
718     auto err = new Exception("yada");
719     a.then((a) {
720         return Promise!void.rejected(err);
721     }).then(() {
722         assert(false);
723     }).except((Exception e) {
724         assert(err is e);
725         called = true;
726     }).nothrow_();
727     assert(!called);
728     a.resolve(2);
729     assert(called);
730 }
731 unittest { //Finally returning a value
732     auto a = new DelegatePromise!int;
733     bool called = false;
734     a.finall(() {
735         return 2;
736     }).then((a) {
737         assert(a == 2);
738         called = true;
739     }).nothrow_();
740     assert(!called);
741     a.resolve(0);
742     assert(called);
743 }
744 unittest { //Fail right away
745     auto a = new DelegatePromiseIterator!int;
746     auto err = new Exception("yada");
747     bool called = false;
748     a.reject(err);
749     a.each((a) {
750         assert(false);
751     }).except((Exception e) {
752         assert(e is err);
753         called = true;
754     }).nothrow_();
755     assert(called);
756 }
757 unittest { //EOF right away
758     auto a = new DelegatePromiseIterator!int;
759     bool called = false;
760     a.resolve();
761     a.each((a) {
762         assert(false);
763     }).then((eof) {
764         assert(eof);
765         called = true;
766     }).nothrow_();
767     assert(called);
768 }
769 unittest { // Ones might re-resolve right away
770     auto a = new DelegatePromiseIterator!int;
771     int calls = 0;
772     auto cont_delay = new DelegatePromise!bool;
773     
774     a.resolve(0).then((cont) {
775         a.resolve(1);
776     }).nothrow_();
777 
778     a.each((b) {
779         assert(b == calls);
780         calls++;
781 
782         if (b == 0) {
783             return Promise!bool.resolved(true);
784         } else {
785             return cont_delay;
786         }
787     }).then((eof) {
788         assert(eof);
789         assert(calls == 2);   
790         calls++;
791     }).nothrow_();
792 
793     a.resolve();
794     assert(calls == 2);
795     cont_delay.resolve(true);
796 }
797 // next() might throw exception
798 unittest {
799 	auto err = new Exception("oi");
800 	auto x = new class PromiseIterator!int {
801 		override Promise!ItValue next(Promise!bool) {
802 			throw err;
803 		}
804 	};
805 
806 	bool called = false;
807 	x.each((_) {
808 		assert(false);
809 	}).then((_) {
810 		assert(false);
811 	}).except((Exception e) {
812 		called = true;
813 		assert(e is err);
814 	}).nothrow_();
815 	assert(called);
816 }
817 private void do_while(U)(U delegate() cb, DelegatePromise!void r, Throwable.TraceInfo backBt) nothrow {
818 	import upromised.backtrace : setBasestack, recoverBasestack;
819 
820 	promisifyCall(cb).then_((v) nothrow {
821 		auto prev = setBasestack(backBt);
822 		scope(exit) recoverBasestack(prev);
823 
824 		if (v.e) {
825 			r.resolve(Promise!void.Value(v.e));
826 			return;
827 		}
828 
829 		bool cont;
830 		static if(is(Promisify!U.T == void)) {
831 			cont = true;
832 		} else {
833 			cont = v.value[0];
834 		}
835 
836 		if (cont) {
837 			do_while(cb, r, backBt);
838 		} else {
839 			r.resolve();
840 		}
841 	});
842 }
843 Promise!void do_while(U)(U delegate() cb) nothrow
844 if (is(Promisify!U.T == bool) || is(Promisify!U.T == void))
845 {
846 	import upromised.backtrace : backtrace, traceinfo, concat;
847 
848 	auto r = new DelegatePromise!void;
849 	Throwable.TraceInfo backBt = ["*async*"].traceinfo.concat(backtrace());
850 	do_while(cb, r, backBt);
851 	return r;
852 }
853 static Promise!bool break_;
854 static Promise!bool continue_;
855 static this() {
856 	break_ = Promise!bool.resolved(false);
857 	continue_ = Promise!bool.resolved(true);
858 }
859 // do_while until return 0
860 unittest {
861 	int count = 3;
862 	do_while(() {
863 		count--;
864 		return count > 0;
865 	}).nothrow_();
866 
867 	assert(count == 0);
868 }
869 // do_while might be delayed
870 unittest {
871 	int called = 0;
872 	DelegatePromise!bool next;
873 	do_while(() {
874 		++called;
875 		next = new DelegatePromise!bool;
876 		return next;
877 	}).then(() {
878 		assert(called == 2);
879 		called++;
880 	}).nothrow_();
881 	assert(called == 1);
882 	next.resolve(true);
883 	assert(called == 2);
884 	next.resolve(false);
885 	assert(called == 3);
886 }
887 // do_while catches all exceptions
888 unittest {
889 	auto err = new Exception("");
890 	bool called;
891 	do_while(() {
892 		throw err;
893 	}).except((Exception e) {
894 		assert(e is err);
895 		called = true;
896 	}).nothrow_();
897 	assert(called);
898 }
899 // do_while with void loops indefinitely
900 unittest {
901 	int called;
902 	DelegatePromise!void next;
903 	do_while(() {
904 		called++;
905 		next  = new DelegatePromise!void;
906 		return next;
907 	}).nothrow_();
908 	assert(called == 1);
909 	next.resolve();
910 	assert(called == 2);
911 	next.resolve();
912 	assert(called == 3);
913 }