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).

// Previously used class "Q" (#1000934) used 328 bytes when idle.
// Now we're at 32 (factor 10+).
// And we can evaporate this even further by merging the queue
// with the object it belongs to. Probably we'll end up paying nothing
// for an idle object while still enjoying the experience of being
// able to start a thread instantly whenever needed.

sclass CompactQ extends Meta is AutoCloseable {

  // Object size at this point: 12 bytes for Java + 4 bytes for Meta
  //   (assuming you have 32 GB or less of Java RAM)
  //
  // So let's keep saving space.
  
  // Step 1 - no name... well, no reserved name field, anyway.
  // Most queues can probably be identified from context.
  // if you want to give your event queue a name - put it in Meta
  // with these convenient procedures:
  
  S name() { ret or2((S) metaGet("name"), toString()); }
  void name(S name) { metaSet(+name); }
  
  // Object size now: Still 16 bytes unless you foolishly insist on a
  // custom queue name which you could definitely achieve in a much
  // more efficient way (hint _subclassing_).
  
  // Now we do need to expend 4 more bytes. The items in the queue
  // have to go somewhere. But!
  
  // No syncLinkedList or something - just an AppendableChain which
  // is a minimalist approach but above the honor level of elegance
  // and flexibility and absolutely not in the way in these circumstances.
  // It's all O(1) after all.
  //
  // All sync is done on the main object BTW. (CompactQ.this)
  
  AppendableChain<Runnable> q;
  
  // Object size now: 20b
  
  // retired also goes to meta. In the end you don't even notice a
  // difference. It's just another setter and getter and they are
  // clearly not hard to make either.
  //
  // Of course, you will notice that we store "unretired" as null
  // (meaning no entry at all, saving all the space) instead of false;
  // even in the case that someone retires their event queue and then
  // _unretires_ it.
  //
  // In the end, one can say we are really really pedantic but we do
  // have a style.
  
  synchronized bool retired() { ret metaGet("retired") != null; }
  synchronized void retired(bool b) { metaPut("retired", trueOrNull(b)); }
  
  // Object size: still 20b. Miraculous!
  //
  // ...Here comes a good one.
  
  // The "enter" field is an optional thread ownership marker, e.g.
  // for modules in the Java OS because otherwise thread control is
  // a nightmare.
  //
  // Well... it still kind of is. But I see the way forward with one
  // clean re-architecture that we should one day just do. We just need
  // central instances of thread managing. This cannot be decentra-
  // lized.
  //
  // Oh, and having slashed absolutely everything else, we keep this
  // one as an actual field.
  //
  // The field type is so a customer's enter procedure can do
  // whatever fancy things they fashion as long as they provide an
  // AutoCloseable that will be called for cleanup.
  
  IF0<AutoCloseable> enter;
  
  // New size: 24b
  
  // We do want to know who is servicing us currently...
  
  Thread thread;       // 28b
  bool startingThread;
  
  // ...and finally the biggest boolean ever brings us to 32 bytes.
  
  // Optional monitoring
  
  ifdef CompactQ_Stats
    new AtomicLong bouts; // how often we have stopped and started
    int biggestSize;      // how long the queue was at the worst moment
  endifdef

  AutoCloseable enter() { ret callF(enter); }
  
  synchronized bool running() { ret thread != null; }

  bool done() { ret !running() && (isEmpty() || retired() || !licensed()); }
  
  // This is way smarter than the half-awake sleep(1) method
  // Basically we take a place at the end of the line and when we're
  // at the counter we see if anyone came in behind us. And repeat
  // if necessary.
  //
  // Note that for this to work reliably we require that items in
  // the queue are auto-closed in case they can't be dispatched
  void waitUntilDone {
    while (!done())
      putFlagInPipe();
  }
  
  void putFlagInPipe() {
    flagSleep(flag -> add(raiseFlagOnRunAndClose(flag)));
  }
  
  // Yes, in JavaX functions can have any number of names
  public synchronized void close aka cancel() {
    retired(true);
    clear();
    if (thread == null) ret;
    // threadBeingCancelled = new WeakReference(thread); // optional
    cancelAndInterruptThread(thread);
    thread = null;
  }
  
  // Forget all the entries but don't cancel or interrupt currently
  // executing task
  public void clear {
    L<Runnable> items;
    synchronized {
      items = cloneList(q);
      q = null;
    }
    cleanUp(items);
  }

  // append to q (do later)
  void add(Runnable r) {
    if (r == null) ret;
    synchronized {
      // cleanly reject if necessary
      if (retired()) ret with cleanUp(r);
      
      q = chainPlus(q, r);
      ifdef CompactQ_Stats biggestSize = max(size(), biggestSize); endifdef
    }
    checkForAction();
  }
  
  // prepend to q (do next)
  void addInFront(Runnable r) {
    if (r == null) ret;
    synchronized {
      // cleanly reject if necessary
      if (retired()) ret with cleanUp(r);
      
      q = itemPlusChain(r, q);
      ifdef CompactQ_Stats biggestSize = max(size(), biggestSize); endifdef
    }
    checkForAction();
  }
  
  protected void checkForAction() {
    synchronized {
      if (done() || running() || startingThread)
        ret;
      // There seems to be work, so we start a thread.
      set startingThread;
    }

    try {
      // At this point, we are no longer synchronized, but
      // we know we are uncontested.
      // No one else will try to start a thread.
      temp enter();
      startThread(name(), () -> {
        try {
          temp enter();

          // Put us in the book.
          synchronized {
            startingThread = false;
            thread = currentThread();
          }

          // And off we go!
          _bout();
        } finally {
          thread = null;
          startingThread = false;
        }
      });
    } on fail {
      close();
    }
  }
  
  synchronized Runnable _grabWork() {
    Runnable r = first(q);
    q = popFirst(q);
    ret r;
  }
  
  // work as long there is work
  void _bout {
    ifdef CompactQ_Stats inc(bouts); endifdef
    while (licensed() && !retired()) {
      Runnable r = _grabWork();
      if (r != null)
        pcall { r.run(); }
      else {
        onIdle();
        
        // Try once more because onIdle may have strategically waited
        // for 1 ms or something to see if more work came in.
        
        if ((r = _grabWork()) != null)
          pcall { r.run(); } // get back in the game
        else
          break;
      }
    }
  }
  
  synchronized bool isEmpty() { ret q == null; }
  synchronized int size() { ret l(q); }
  
  O mutex() { this; } // clients can synchronize on this
  
  // export procedure following JavaX synchronization rules
  // (only synchronize on one object at a time. exceptions granted
  // when proof of harmlessness is given.)
  synchronized L<Runnable> snapshot() { ret cloneList_unsynced(q); }
  
  // you can override this
  void onIdle {}
}

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: 281 / 653
Version history: 75 change(s)
Referenced in: [show references]