1 /**
2 *
3 * Various @nogc alternatives. This file includes parts of std.process, std.random, std.uuid.
4 *
5 * Authors:
6 *    $(HTTP guillaumepiolat.fr, Guillaume Piolat)
7 *    $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad),
8 *    $(LINK2 https://github.com/schveiguy, Steven Schveighoffer),
9 *    $(HTTP thecybershadow.net, Vladimir Panteleev)
10 *
11 * Copyright:
12 *   Copyright (c) 2016, Auburn Sounds.
13 *   Copyright (c) 2013, Lars Tandle Kyllingstad (std.process).
14 *   Copyright (c) 2013, Steven Schveighoffer (std.process).
15 *   Copyright (c) 2013, Vladimir Panteleev (std.process).
16 *
17 * License:
18 *   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
19 */
20 module dplug.core.nogc;
21 
22 import core.stdc.string: strdup, memcpy;
23 import core.stdc.stdlib: malloc, free, getenv;
24 import core.memory: GC;
25 import core.exception: onOutOfMemoryErrorNoGC;
26 
27 import std.conv: emplace;
28 import std.traits;
29 import std.array: empty;
30 import std.exception: assumeUnique;
31 
32 // This module provides many utilities to deal with @nogc
33 
34 version = doNotUseRuntime;
35 
36 //
37 // Faking @nogc
38 //
39 
40 auto assumeNoGC(T) (T t)
41 {
42     static if (isFunctionPointer!T || isDelegate!T)
43     {
44         enum attrs = functionAttributes!T | FunctionAttribute.nogc;
45         return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
46     }
47     else
48         static assert(false);
49 }
50 
51 auto assumeNothrowNoGC(T) (T t)
52 {
53     static if (isFunctionPointer!T || isDelegate!T)
54     {
55         enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_;
56         return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
57     }
58     else
59         static assert(false);
60 }
61 
62 unittest
63 {
64     void funcThatDoesGC()
65     {
66         int a = 4;
67         int[] b = [a, a, a];
68     }
69 
70     void anotherFunction() nothrow @nogc
71     {
72         assumeNothrowNoGC( (){ funcThatDoesGC(); } )();
73     }
74 
75     void aThirdFunction() @nogc
76     {
77         assumeNoGC( () { funcThatDoesGC(); } )();
78     }
79 }
80 
81 
82 //
83 // Optimistic .destroy, which is @nogc nothrow by breaking the type-system
84 //
85 
86 // for classes
87 void destroyNoGC(T)(T x) nothrow @nogc if (is(T == class) || is(T == interface))
88 {
89     assumeNothrowNoGC(
90         (T x)
91         {
92             return destroy(x);
93         })(x);
94 }
95 
96 // for struct
97 void destroyNoGC(T)(ref T obj) nothrow @nogc if (is(T == struct))
98 {
99     assumeNothrowNoGC(
100         (ref T x)
101         {
102             return destroy(x);
103         })(obj);
104 }
105 /*
106 void destroyNoGC(T : U[n], U, size_t n)(ref T obj) nothrow @nogc
107 {
108     assumeNothrowNoGC(
109         (T x)
110         {
111             return destroy(x);
112         })(obj);
113 }*/
114 
115 void destroyNoGC(T)(ref T obj) nothrow @nogc
116     if (!is(T == struct) && !is(T == class) && !is(T == interface))
117 {
118     assumeNothrowNoGC(
119                       (ref T x)
120                       {
121                           return destroy(x);
122                       })(obj);
123 }
124 
125 
126 
127 //
128 // Constructing and destroying without the GC.
129 //
130 
131 /// Allocates and construct a struct or class object.
132 /// Returns: Newly allocated object.
133 auto mallocEmplace(T, Args...)(Args args)
134 {
135     static if (is(T == class))
136         immutable size_t allocSize = __traits(classInstanceSize, T);
137     else
138         immutable size_t allocSize = T.sizeof;
139 
140     void* rawMemory = malloc(allocSize);
141     if (!rawMemory)
142         onOutOfMemoryErrorNoGC();
143 
144     version(doNotUseRuntime)
145     {
146     }
147     else
148     {
149         static if (hasIndirections!T)
150             GC.addRange(rawMemory, allocSize);
151     }
152 
153     static if (is(T == class))
154     {
155         T obj = emplace!T(rawMemory[0 .. allocSize], args);
156     }
157     else
158     {
159         T* obj = cast(T*)rawMemory;
160         emplace!T(obj, args);
161     }
162 
163     return obj;
164 }
165 
166 /// Destroys and frees a class object created with $(D mallocEmplace).
167 void destroyFree(T)(T p) if (is(T == class))
168 {
169     if (p !is null)
170     {
171         destroyNoGC(p);
172 
173         version(doNotUseRuntime)
174         {
175         }
176         else
177         {
178             static if (hasIndirections!T)
179                 GC.removeRange(cast(void*)p);
180         }
181 
182         free(cast(void*)p);
183     }
184 }
185 
186 /// Destroys and frees an interface object created with $(D mallocEmplace).
187 void destroyFree(T)(T p) if (is(T == interface))
188 {
189     if (p !is null)
190     {
191         void* here = cast(void*)(cast(Object)p);
192         destroyNoGC(p);
193 
194         version(doNotUseRuntime)
195         {
196         }
197         else
198         {
199             static if (hasIndirections!T)
200                 GC.removeRange(here);
201         }
202 
203         free(cast(void*)here);
204     }
205 }
206 
207 /// Destroys and frees a non-class object created with $(D mallocEmplace).
208 void destroyFree(T)(T* p) if (!is(T == class))
209 {
210     if (p !is null)
211     {
212         destroyNoGC(p);
213 
214         version(doNotUseRuntime)
215         {
216         }
217         else
218         {
219             static if (hasIndirections!T)
220                 GC.removeRange(cast(void*)p);
221         }
222 
223         free(cast(void*)p);
224     }
225 }
226 
227 
228 unittest
229 {
230     class A
231     {
232         int _i;
233         this(int i)
234         {
235             _i = i;
236         }
237     }
238 
239     struct B
240     {
241         int i;
242     }
243 
244     void testMallocEmplace()
245     {
246         A a = mallocEmplace!A(4);
247         destroyFree(a);
248 
249         B* b = mallocEmplace!B(5);
250         destroyFree(b);
251     }
252 
253     testMallocEmplace();
254 }
255 
256 version( D_InlineAsm_X86 )
257 {
258     version = AsmX86;
259 }
260 else version( D_InlineAsm_X86_64 )
261 {
262     version = AsmX86;
263 }
264 
265 /// Allocates a slice with `malloc`.
266 /// This does not add GC roots so when using the runtime do not use such slice as traceable.
267 T[] mallocSlice(T)(size_t count) nothrow @nogc
268 {
269     T[] slice = mallocSliceNoInit!T(count);
270     static if (is(T == struct))
271     {
272         // we must avoid calling struct destructors with uninitialized memory
273         for(size_t i = 0; i < count; ++i)
274         {
275             T uninitialized;
276             memcpy(&slice[i], &uninitialized, T.sizeof);
277         }
278     }
279     else
280         slice[0..count] = T.init;
281     return slice;
282 }
283 
284 /// Allocates a slice with `malloc`, but does not initialize the content.
285 /// This does not add GC roots so when using the runtime do not use such slice as traceable.
286 T[] mallocSliceNoInit(T)(size_t count) nothrow @nogc
287 {
288     T* p = cast(T*) malloc(count * T.sizeof);
289     return p[0..count];
290 }
291 
292 /// Free a slice allocated with `mallocSlice`.
293 void freeSlice(T)(const(T)[] slice) nothrow @nogc
294 {
295     free(cast(void*)(slice.ptr)); // const cast here
296 }
297 
298 // Duplicates a slice with malloc. Equivalent to .dup
299 T[] mallocDup(T)(const(T)[] slice) nothrow @nogc if (!is(T == struct))
300 {
301     T[] copy = mallocSliceNoInit!T(slice.length);
302     memcpy(copy.ptr, slice.ptr, slice.length * T.sizeof);
303     return copy;
304 }
305 
306 // Duplicates a slice with malloc. Equivalent to .idup
307 immutable(T)[] mallocIDup(T)(const(T)[] slice) nothrow @nogc if (!is(T == struct))
308 {
309     T[] copy = mallocSliceNoInit!T(slice.length);
310     memcpy(copy.ptr, slice.ptr, slice.length * T.sizeof);
311     return assumeUnique(copy);
312 }
313 
314 unittest
315 {
316     int[] slice = mallocSlice!int(4);
317     freeSlice(slice);
318     assert(slice[3] == int.init);
319 
320     slice = mallocSliceNoInit!int(4);
321     freeSlice(slice);
322 
323     slice = mallocSliceNoInit!int(0);
324     assert(slice == []);
325     freeSlice(slice);
326 }
327 
328 
329 //
330 // GC-proof resources: for when the GC does exist.
331 //
332 
333 /// Destructors called by the GC enjoy a variety of limitations and
334 /// relying on them is dangerous.
335 /// See_also: $(WEB p0nce.github.io/d-idioms/#The-trouble-with-class-destructors)
336 /// Example:
337 /// ---
338 /// class Resource
339 /// {
340 ///     ~this()
341 ///     {
342 ///         if (!alreadyClosed)
343 ///         {
344 ///             if (isCalledByGC())
345 ///                 assert(false, "Resource release relies on Garbage Collection");
346 ///             alreadyClosed = true;
347 ///             releaseResource();
348 ///         }
349 ///     }
350 /// }
351 /// ---
352 bool isCalledByGC() nothrow
353 {
354     import core.exception;
355     try
356     {
357         import core.memory;
358         cast(void) GC.malloc(1); // not ideal since it allocates
359         return false;
360     }
361     catch(InvalidMemoryOperationError e)
362     {
363         return true;
364     }
365 }
366 
367 unittest
368 {
369     class A
370     {
371         ~this()
372         {
373             assert(!isCalledByGC());
374         }
375     }
376     import std.typecons;
377     auto a = scoped!A();
378 }
379 
380 
381 
382 
383 //
384 // @nogc sorting.
385 //
386 
387 /// Must return -1 if a < b
388 ///              0 if a == b
389 ///              1 if a > b
390 alias nogcComparisonFunction(T) = int delegate(in T a, in T b) nothrow @nogc;
391 
392 /// @nogc quicksort
393 /// From the excellent: http://codereview.stackexchange.com/a/77788
394 void quicksort(T)(T[] array, nogcComparisonFunction!T comparison) nothrow @nogc
395 {
396     if (array.length < 2)
397         return;
398 
399     static void swapElem(ref T lhs, ref T rhs)
400     {
401         T tmp = lhs;
402         lhs = rhs;
403         rhs = tmp;
404     }
405 
406     int partition(T* arr, int left, int right) nothrow @nogc
407     {
408         immutable int mid = left + (right - left) / 2;
409         T pivot = arr[mid];
410         // move the mid point value to the front.
411         swapElem(arr[mid],arr[left]);
412         int i = left + 1;
413         int j = right;
414         while (i <= j)
415         {
416             while(i <= j && comparison(arr[i], pivot) <= 0 )
417                 i++;
418 
419             while(i <= j && comparison(arr[j], pivot) > 0)
420                 j--;
421 
422             if (i < j)
423                 swapElem(arr[i], arr[j]);
424         }
425         swapElem(arr[i - 1], arr[left]);
426         return i - 1;
427     }
428 
429     void doQsort(T* array, int left, int right) nothrow @nogc
430     {
431         if (left >= right)
432             return;
433 
434         int part = partition(array, left, right);
435         doQsort(array, left, part - 1);
436         doQsort(array, part + 1, right);
437     }
438 
439     doQsort(array.ptr, 0, cast(int)(array.length) - 1);
440 }
441 
442 unittest
443 {
444     int[] testData = [110, 5, 10, 3, 22, 100, 1, 23];
445     quicksort!int(testData, (a, b) => (a - b));
446     assert(testData == [1, 3, 5, 10, 22, 23, 100, 110]);
447 }
448 
449 
450 //
451 // STABLE MERGE SORT
452 //
453 
454 // Stable merge sort, using a temporary array.
455 // Array A[] has the items to sort.
456 // Array B[] is a work array.
457 void mergeSort(T)(T[] inoutElements, T[] scratchBuffer, nogcComparisonFunction!T comparison) nothrow @nogc
458 {
459     // Left source half is A[ iBegin:iMiddle-1].
460     // Right source half is A[iMiddle:iEnd-1   ].
461     // Result is            B[ iBegin:iEnd-1   ].
462     void topDownMerge(T)(T* A, int iBegin, int iMiddle, int iEnd, T* B) nothrow @nogc
463     {
464         int i = iBegin;
465         int j = iMiddle;
466 
467         // While there are elements in the left or right runs...
468         for (int k = iBegin; k < iEnd; k++)
469         {
470             // If left run head exists and is <= existing right run head.
471             if ( i < iMiddle && ( j >= iEnd || (comparison(A[i], A[j]) <= 0) ) )
472             {
473                 B[k] = A[i];
474                 i = i + 1;
475             }
476             else
477             {
478                 B[k] = A[j];
479                 j = j + 1;
480             }
481         }
482     }
483 
484     // Sort the given run of array A[] using array B[] as a source.
485     // iBegin is inclusive; iEnd is exclusive (A[iEnd] is not in the set).
486     void topDownSplitMerge(T)(T* B, int iBegin, int iEnd, T* A) nothrow @nogc
487     {
488         if(iEnd - iBegin < 2)                       // if run size == 1
489             return;                                 //   consider it sorted
490         // split the run longer than 1 item into halves
491         int iMiddle = (iEnd + iBegin) / 2;              // iMiddle = mid point
492         // recursively sort both runs from array A[] into B[]
493         topDownSplitMerge!T(A, iBegin,  iMiddle, B);  // sort the left  run
494         topDownSplitMerge!T(A, iMiddle,    iEnd, B);  // sort the right run
495         // merge the resulting runs from array B[] into A[]
496         topDownMerge!T(B, iBegin, iMiddle, iEnd, A);
497     }
498 
499     assert(inoutElements.length == scratchBuffer.length);
500     int n = cast(int)inoutElements.length;
501     scratchBuffer[] = inoutElements[]; // copy data into temporary buffer
502     topDownSplitMerge(scratchBuffer.ptr, 0, n, inoutElements.ptr);
503 }
504 
505 unittest
506 {
507     int[2][] scratch;
508     scratch.length = 8;
509     int[2][] testData = [[110, 0], [5, 0], [10, 0], [3, 0], [110, 1], [5, 1], [10, 1], [3, 1]];
510     mergeSort!(int[2])(testData, scratch, (a, b) => (a[0] - b[0]));
511     assert(testData == [[3, 0], [3, 1], [5, 0], [5, 1], [10, 0], [10, 1], [110, 0], [110, 1]]);
512 }
513 
514 
515 
516 //
517 // Unrelated things that hardly goes anywhere
518 //
519 
520 /// Crash if the GC is running.
521 /// Useful in destructors to avoid reliance GC resource release.
522 /// However since this is not @nogc, this is not suitable in runtime-less D.
523 /// See_also: $(WEB p0nce.github.io/d-idioms/#GC-proof-resource-class)
524 void ensureNotInGC(string resourceName = null) nothrow
525 {
526     import core.exception;
527     try
528     {
529         import core.memory;
530         cast(void) GC.malloc(1); // not ideal since it allocates
531         return;
532     }
533     catch(InvalidMemoryOperationError e)
534     {
535         import core.stdc.stdio;
536         fprintf(stderr, "Error: clean-up of %s incorrectly depends on destructors called by the GC.\n",
537                 resourceName ? resourceName.ptr : "a resource".ptr);
538         assert(false); // crash
539     }
540 }
541 
542 
543 
544 /// To call for something that should never happen, but we still
545 /// want to make a "best effort" at runtime even if it can be meaningless.
546 /// MAYDO: change that name, it's not actually unrecoverable
547 /// MAYDO: stop using that function
548 void unrecoverableError() nothrow @nogc
549 {
550     debug
551     {
552         // Crash unconditionally
553         assert(false);
554     }
555     else
556     {
557         // There is a trade-off here, if we crash immediately we will be
558         // correctly identified by the user as the origin of the bug, which
559         // is always helpful.
560         // But crashing may in many-case also crash the host, which is not very friendly.
561         // Eg: a plugin not instancing vs host crashing.
562         // The reasoning is that the former is better from the user POV.
563     }
564 }
565 
566 
567 /// A bit faster than a dynamic cast.
568 /// This is to avoid TypeInfo look-up.
569 T unsafeObjectCast(T)(Object obj)
570 {
571     return cast(T)(cast(void*)(obj));
572 }
573 
574 
575 /// Inserts a breakpoint instruction. useful to trigger the debugger.
576 void debugBreak() nothrow @nogc
577 {
578     version( AsmX86 )
579     {
580         asm nothrow @nogc
581         {
582             int 3;
583         }
584     }
585     else version( GNU )
586     {
587         // __builtin_trap() is not the same thing unfortunately
588         asm
589         {
590             "int $0x03" : : : ;
591         }
592     }
593     else
594     {
595         static assert(false, "No debugBreak() for this compiler");
596     }
597 }
598 
599 
600 // Copy source into dest.
601 // dest must contain room for maxChars characters
602 // A zero-byte character is then appended.
603 void stringNCopy(char* dest, size_t maxChars, const(char)[] source) nothrow @nogc
604 {
605     if (maxChars == 0)
606         return;
607 
608     size_t max = maxChars < source.length ? maxChars - 1 : source.length;
609     for (int i = 0; i < max; ++i)
610         dest[i] = source[i];
611     dest[max] = '\0';
612 }
613 
614 
615 //
616 // Low-cost C string conversions
617 //
618 alias CString = CStringImpl!char;
619 alias CString16 = CStringImpl!wchar;
620 
621 /// Zero-terminated C string, to replace toStringz and toUTF16z
622 struct CStringImpl(CharType) if (is(CharType: char) || is(CharType: wchar))
623 {
624 public:
625 nothrow:
626 @nogc:
627 
628     const(CharType)* storage = null;
629     alias storage this;
630 
631 
632     this(const(CharType)[] s)
633     {
634         // Always copy. We can't assume anything about the input.
635         size_t len = s.length;
636         CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof);
637         buffer[0..len] = s[0..len];
638         buffer[len] = '\0';
639         storage = buffer;
640         wasAllocated = true;
641     }
642 
643     // The constructor taking immutable can safely assume that such memory
644     // has been allocated by the GC or malloc, or an allocator that align
645     // pointer on at least 4 bytes.
646     this(immutable(CharType)[] s)
647     {
648         // Same optimizations that for toStringz
649         if (s.empty)
650         {
651             enum emptyString = cast(CharType[])"";
652             storage = emptyString.ptr;
653             return;
654         }
655 
656         /* Peek past end of s[], if it's 0, no conversion necessary.
657         * Note that the compiler will put a 0 past the end of static
658         * strings, and the storage allocator will put a 0 past the end
659         * of newly allocated char[]'s.
660         */
661         const(CharType)* p = s.ptr + s.length;
662         // Is p dereferenceable? A simple test: if the p points to an
663         // address multiple of 4, then conservatively assume the pointer
664         // might be pointing to another block of memory, which might be
665         // unreadable. Otherwise, it's definitely pointing to valid
666         // memory.
667         if ((cast(size_t) p & 3) && *p == 0)
668         {
669             storage = s.ptr;
670             return;
671         }
672 
673         size_t len = s.length;
674         CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof);
675         buffer[0..len] = s[0..len];
676         buffer[len] = '\0';
677         storage = buffer;
678         wasAllocated = true;
679     }
680 
681     ~this()
682     {
683         if (wasAllocated)
684             free(cast(void*)storage);
685     }
686 
687     @disable this(this);
688 
689 private:
690     bool wasAllocated = false;
691 }
692 
693 
694 //
695 // Launch browser, replaces std.process.browse
696 //
697 
698 void browseNoGC(string url) nothrow @nogc
699 {
700     version(Windows)
701     {
702         import core.sys.windows.winuser;
703         import core.sys.windows.shellapi;
704         ShellExecuteA(null, CString("open").storage, CString(url).storage, null, null, SW_SHOWNORMAL);
705     }
706 
707     version(OSX)
708     {
709         import core.sys.posix.unistd;
710         const(char)*[5] args;
711 
712         auto curl = CString(url).storage;
713         const(char)* browser = getenv("BROWSER");
714         if (browser)
715         {
716             browser = strdup(browser);
717             args[0] = browser;
718             args[1] = curl;
719             args[2] = null;
720         }
721         else
722         {
723             args[0] = "open".ptr;
724             args[1] = curl;
725             args[2] = null;
726         }
727 
728         auto childpid = core.sys.posix.unistd.fork();
729         if (childpid == 0)
730         {
731             core.sys.posix.unistd.execvp(args[0], cast(char**)args.ptr);
732             return;
733         }
734         if (browser)
735             free(cast(void*)browser);
736     }
737 }
738