Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

237
LINES

< > BotCompany Repo | #1032857 // CompactQ - Q (thread-based queue) but in compact - only 32 bytes when idle (including Java header!)

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (6432L/37K).

1  
// Previously used class "Q" (#1000934) used 328 bytes when idle.
2  
// Now we're at 32 (factor 10+).
3  
// And we can evaporate this even further by merging the queue
4  
// with the object it belongs to. Probably we'll end up paying nothing
5  
// for an idle object while still enjoying the experience of being
6  
// able to start a thread instantly whenever needed.
7  
8  
sclass CompactQ extends Meta is AutoCloseable {
9  
10  
  // Object size at this point: 12 bytes for Java + 4 bytes for Meta
11  
  //   (assuming you have 32 GB or less of Java RAM)
12  
  //
13  
  // So let's keep saving space.
14  
  
15  
  // Step 1 - no name... well, no reserved name field, anyway.
16  
  // Most queues can probably be identified from context.
17  
  // if you want to give your event queue a name - put it in Meta
18  
  // with these convenient procedures:
19  
  
20  
  S name() { ret or2((S) metaGet("name"), toString()); }
21  
  void name(S name) { metaSet(+name); }
22  
  
23  
  // Object size now: Still 16 bytes unless you foolishly insist on a
24  
  // custom queue name which you could definitely achieve in a much
25  
  // more efficient way (hint _subclassing_).
26  
  
27  
  // Now we do need to expend 4 more bytes. The items in the queue
28  
  // have to go somewhere. But!
29  
  
30  
  // No syncLinkedList or something - just an AppendableChain which
31  
  // is a minimalist approach but above the honor level of elegance
32  
  // and flexibility and absolutely not in the way in these circumstances.
33  
  // It's all O(1) after all.
34  
  //
35  
  // All sync is done on the main object BTW. (CompactQ.this)
36  
  
37  
  AppendableChain<Runnable> q;
38  
  
39  
  // Object size now: 20b
40  
  
41  
  // retired also goes to meta. In the end you don't even notice a
42  
  // difference. It's just another setter and getter and they are
43  
  // clearly not hard to make either.
44  
  //
45  
  // Of course, you will notice that we store "unretired" as null
46  
  // (meaning no entry at all, saving all the space) instead of false;
47  
  // even in the case that someone retires their event queue and then
48  
  // _unretires_ it.
49  
  //
50  
  // In the end, one can say we are really really pedantic but we do
51  
  // have a style.
52  
  
53  
  synchronized bool retired() { ret metaGet("retired") != null; }
54  
  synchronized void retired(bool b) { metaPut("retired", trueOrNull(b)); }
55  
  
56  
  // Object size: still 20b. Miraculous!
57  
  //
58  
  // ...Here comes a good one.
59  
  
60  
  // The "enter" field is an optional thread ownership marker, e.g.
61  
  // for modules in the Java OS because otherwise thread control is
62  
  // a nightmare.
63  
  //
64  
  // Well... it still kind of is. But I see the way forward with one
65  
  // clean re-architecture that we should one day just do. We just need
66  
  // central instances of thread managing. This cannot be decentra-
67  
  // lized.
68  
  //
69  
  // Oh, and having slashed absolutely everything else, we keep this
70  
  // one as an actual field.
71  
  //
72  
  // The field type is so a customer's enter procedure can do
73  
  // whatever fancy things they fashion as long as they provide an
74  
  // AutoCloseable that will be called for cleanup.
75  
  
76  
  IF0<AutoCloseable> enter;
77  
  
78  
  // New size: 24b
79  
  
80  
  // We do want to know who is servicing us currently...
81  
  
82  
  Thread thread;       // 28b
83  
  bool startingThread;
84  
  
85  
  // ...and finally the biggest boolean ever brings us to 32 bytes.
86  
  
87  
  // Optional monitoring
88  
  
89  
  ifdef CompactQ_Stats
90  
    new AtomicLong bouts; // how often we have stopped and started
91  
    int biggestSize;      // how long the queue was at the worst moment
92  
  endifdef
93  
94  
  AutoCloseable enter() { ret callF(enter); }
95  
  
96  
  synchronized bool running() { ret thread != null; }
97  
98  
  bool done() { ret !running() && (isEmpty() || retired() || !licensed()); }
99  
  
100  
  // This is way smarter than the half-awake sleep(1) method
101  
  // Basically we take a place at the end of the line and when we're
102  
  // at the counter we see if anyone came in behind us. And repeat
103  
  // if necessary.
104  
  //
105  
  // Note that for this to work reliably we require that items in
106  
  // the queue are auto-closed in case they can't be dispatched
107  
  void waitUntilDone {
108  
    while (!done())
109  
      putFlagInPipe();
110  
  }
111  
  
112  
  void putFlagInPipe() {
113  
    flagSleep(flag -> add(raiseFlagOnRunAndClose(flag)));
114  
  }
115  
  
116  
  // Yes, in JavaX functions can have any number of names
117  
  public synchronized void close aka cancel() {
118  
    retired(true);
119  
    clear();
120  
    if (thread == null) ret;
121  
    // threadBeingCancelled = new WeakReference(thread); // optional
122  
    cancelAndInterruptThread(thread);
123  
    thread = null;
124  
  }
125  
  
126  
  // Forget all the entries but don't cancel or interrupt currently
127  
  // executing task
128  
  public void clear {
129  
    L<Runnable> items;
130  
    synchronized {
131  
      items = cloneList(q);
132  
      q = null;
133  
    }
134  
    cleanUp(items);
135  
  }
136  
137  
  // append to q (do later)
138  
  void add(Runnable r) {
139  
    if (r == null) ret;
140  
    synchronized {
141  
      // cleanly reject if necessary
142  
      if (retired()) ret with cleanUp(r);
143  
      
144  
      q = chainPlus(q, r);
145  
      ifdef CompactQ_Stats biggestSize = max(size(), biggestSize); endifdef
146  
    }
147  
    checkForAction();
148  
  }
149  
  
150  
  // prepend to q (do next)
151  
  void addInFront(Runnable r) {
152  
    if (r == null) ret;
153  
    synchronized {
154  
      // cleanly reject if necessary
155  
      if (retired()) ret with cleanUp(r);
156  
      
157  
      q = itemPlusChain(r, q);
158  
      ifdef CompactQ_Stats biggestSize = max(size(), biggestSize); endifdef
159  
    }
160  
    checkForAction();
161  
  }
162  
  
163  
  protected void checkForAction() {
164  
    synchronized {
165  
      if (done() || running() || startingThread)
166  
        ret;
167  
      // There seems to be work, so we start a thread.
168  
      set startingThread;
169  
    }
170  
171  
    try {
172  
      // At this point, we are no longer synchronized, but
173  
      // we know we are uncontested.
174  
      // No one else will try to start a thread.
175  
      temp enter();
176  
      startThread(name(), () -> {
177  
        try {
178  
          temp enter();
179  
180  
          // Put us in the book.
181  
          synchronized {
182  
            startingThread = false;
183  
            thread = currentThread();
184  
          }
185  
186  
          // And off we go!
187  
          _bout();
188  
        } finally {
189  
          thread = null;
190  
          startingThread = false;
191  
        }
192  
      });
193  
    } on fail {
194  
      close();
195  
    }
196  
  }
197  
  
198  
  synchronized Runnable _grabWork() {
199  
    Runnable r = first(q);
200  
    q = popFirst(q);
201  
    ret r;
202  
  }
203  
  
204  
  // work as long there is work
205  
  void _bout {
206  
    ifdef CompactQ_Stats inc(bouts); endifdef
207  
    while (licensed() && !retired()) {
208  
      Runnable r = _grabWork();
209  
      if (r != null)
210  
        pcall { r.run(); }
211  
      else {
212  
        onIdle();
213  
        
214  
        // Try once more because onIdle may have strategically waited
215  
        // for 1 ms or something to see if more work came in.
216  
        
217  
        if ((r = _grabWork()) != null)
218  
          pcall { r.run(); } // get back in the game
219  
        else
220  
          break;
221  
      }
222  
    }
223  
  }
224  
  
225  
  synchronized bool isEmpty() { ret q == null; }
226  
  synchronized int size() { ret l(q); }
227  
  
228  
  O mutex() { this; } // clients can synchronize on this
229  
  
230  
  // export procedure following JavaX synchronization rules
231  
  // (only synchronize on one object at a time. exceptions granted
232  
  // when proof of harmlessness is given.)
233  
  synchronized L<Runnable> snapshot() { ret cloneList_unsynced(q); }
234  
  
235  
  // you can override this
236  
  void onIdle {}
237  
}

Author comment

Began life as a copy of #1000934

download  show line numbers  debug dex  old transpilations   

Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1032857
Snippet name: CompactQ - Q (thread-based queue) but in compact - only 32 bytes when idle (including Java header!)
Eternal ID of this version: #1032857/76
Text MD5: 32da1580b4fb6dbd8109b9d1b1e8aaa0
Transpilation MD5: 8972134aa4e72bed0ed2fa022e28a3a7
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-10-19 21:48:36
Source code size: 7139 bytes / 237 lines
Pitched / IR pitched: No / No
Views / Downloads: 291 / 665
Version history: 75 change(s)
Referenced in: [show references]