1 /**
2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 Trim the heap size by periodically force the GC to collect unused memory, free
7 it and then tell malloc to further free it back to the OS.
8 */
9 module my.gc.memfree;
10 
11 import std.concurrency : send, spawn, receiveTimeout, Tid;
12 import std.datetime : SysTime, Clock, dur;
13 
14 import my.gc.refc;
15 
16 /// Returns: a started instance of MemFree.
17 RefCounted!MemFree memFree() @safe {
18     MemFree inst;
19     inst.start;
20     return RefCounted!MemFree(inst);
21 }
22 
23 /** Reduces the used memory by the GC and free the heap to the OS.
24  *
25  * To avoid calling this too often the struct have a timer to ensure it is
26  * callled at most ones every minute.
27  *
28  * TODO: maybe add functionality to call it more often when above e.g. 50% memory usage?
29  */
30 struct MemFree {
31     private {
32         bool isRunning;
33         Tid bg;
34     }
35 
36     ~this() @trusted {
37         if (!isRunning)
38             return;
39 
40         scope (exit)
41             isRunning = false;
42         send(bg, Msg.stop);
43     }
44 
45     /** Start a background thread to do the work.
46      *
47      * It terminates when the destructor is called.
48      */
49     void start() @trusted {
50         bg = spawn(&tick);
51         isRunning = true;
52     }
53 
54 }
55 
56 private:
57 
58 enum Msg {
59     stop,
60 }
61 
62 void tick() nothrow {
63     import core.thread : Thread;
64     import core.time : dur;
65     import core.memory : GC;
66 
67     const tickInterval = 1.dur!"minutes";
68 
69     bool running = true;
70     SysTime next = Clock.currTime + tickInterval;
71     while (running) {
72         try {
73             receiveTimeout(tickInterval, (Msg x) { running = false; });
74         } catch (Exception e) {
75             running = false;
76         }
77 
78         GC.collect;
79         GC.minimize;
80         malloc_trim(0);
81     }
82 }
83 
84 // malloc_trim - release free memory from the heap
85 extern (C) int malloc_trim(size_t pad) nothrow @system;