1 /** 2 * IO related functions 3 */ 4 5 module unit_threaded.io; 6 7 import std.concurrency; 8 import std.stdio; 9 import std.conv; 10 11 12 private shared(bool) _debugOutput = false; ///whether or not to print debug messages 13 private shared(bool) _forceEscCodes = false; ///whether or not to use ANSI escape codes anyway 14 15 16 package void enableDebugOutput() { 17 synchronized { 18 _debugOutput = true; 19 } 20 } 21 22 package bool isDebugOutputEnabled() { 23 synchronized { 24 return _debugOutput; 25 } 26 } 27 28 package void forceEscCodes() { 29 _forceEscCodes = true; 30 } 31 32 void addToOutput(ref string output, in string msg) { 33 if(_debugOutput) { 34 import std.stdio; 35 writeln(msg); 36 } else { 37 output ~= msg; 38 } 39 } 40 41 /** 42 * Write if debug output was enabled. Not thread-safe in the sense that it 43 * will get printed out immediately and may overlap with other output. 44 */ 45 void writelnUt(T...)(T args) { 46 import std.stdio; 47 if(_debugOutput) writeln(" ", args); 48 } 49 50 package void utWrite(T...)(T args) { 51 WriterThread.get().write(args); 52 } 53 54 package void utWriteln(T...)(T args) { 55 WriterThread.get().writeln(args); 56 } 57 58 package void utWritelnGreen(T...)(T args) { 59 WriterThread.get().writelnGreen(args); 60 } 61 62 package void utWritelnRed(T...)(T args) { 63 WriterThread.get().writelnRed(args); 64 } 65 66 package void utWriteRed(T...)(T args) { 67 WriterThread.get().writeRed(args); 68 } 69 70 package void utWriteYellow(T...)(T args) { 71 WriterThread.get().writeYellow(args); 72 } 73 74 75 /** 76 * Thread to output to stdout 77 */ 78 class WriterThread { 79 static WriterThread get() { 80 if(!_instantiated) { 81 synchronized { 82 if (_instance is null) { 83 _instance = new WriterThread; 84 } 85 _instantiated = true; 86 } 87 } 88 return _instance; 89 } 90 91 void write(T...)(T args) { 92 _tid.send(text(args)); 93 } 94 95 void writeln(T...)(T args) { 96 write(args, "\n"); 97 } 98 99 void writelnGreen(T...)(T args) { 100 _tid.send(green(text(args) ~ "\n")); 101 } 102 103 void writelnRed(T...)(T args) { 104 _tid.send(red(text(args) ~ "\n")); 105 } 106 107 void writeRed(T...)(T args) { 108 _tid.send(red(text(args))); 109 } 110 111 void writeYellow(T...)(T args) { 112 _tid.send(yellow(text(args))); 113 } 114 115 void join() { 116 _tid.send(thisTid); //tell it to join 117 receiveOnly!Tid(); //wait for it to join 118 } 119 120 private: 121 122 this() { 123 _tid = spawn(&threadWriter); 124 _escCodes = [ "red": "\033[31;1m", 125 "green": "\033[32;1m", 126 "yellow": "\033[33;1m", 127 "cancel": "\033[0;;m" ]; 128 129 version(Posix) { 130 import core.sys.posix.unistd; 131 _useEscCodes = _forceEscCodes || isatty(stdout.fileno()) != 0; 132 } 133 } 134 135 /** 136 * Generates coloured output on POSIX systems 137 */ 138 string green(in string msg) const { 139 return escCode("green") ~ msg ~ escCode("cancel"); 140 } 141 142 string red(in string msg) const { 143 return escCode("red") ~ msg ~ escCode("cancel"); 144 } 145 146 string yellow(in string msg) const { 147 return escCode("yellow") ~ msg ~ escCode("cancel"); 148 } 149 150 string escCode(in string code) const { 151 return _useEscCodes ? _escCodes[code] : ""; 152 } 153 154 155 Tid _tid; 156 string[string] _escCodes; 157 bool _useEscCodes; 158 159 static bool _instantiated; // Thread local 160 __gshared WriterThread _instance; 161 } 162 163 private void threadWriter() { 164 auto done = false; 165 Tid tid; 166 167 auto saveStdout = stdout; 168 auto saveStderr = stderr; 169 170 if(!isDebugOutputEnabled()) { 171 version(Posix) { 172 enum nullFileName = "/dev/null"; 173 } else { 174 enum nullFileName = "NUL"; 175 } 176 177 stdout = File(nullFileName, "w"); 178 stderr = File(nullFileName, "w"); 179 } 180 181 while(!done) { 182 string output; 183 receive( 184 (string msg) { 185 output ~= msg; 186 }, 187 (Tid i) { 188 done = true; 189 tid = i; 190 }, 191 (OwnerTerminated trm) { 192 done = true; 193 } 194 ); 195 saveStdout.write(output); 196 } 197 saveStdout.flush(); 198 stdout = saveStdout; 199 stderr = saveStderr; 200 if(tid != Tid.init) tid.send(thisTid); 201 }