Warning: session_start(): open(/var/lib/php/sessions/sess_2nqsjl2kchaa3ullk7sc5oartl, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
// Previous version (#1000934) was 328 bytes when idle.
// Now we're at 32.
// 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
AppendableChain 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.
bool retired() { ret metaGet("retired") != null; }
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 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.
AutoCloseable enter() { ret callF(enter); }
synchronized bool running() { ret thread != null; }
bool done() { ret retired() || !licensed() || isEmpty(); }
void waitUntilDone {
while (!done()) sleep(1);
}
// 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 synchronized void clear {
q = null;
}
// append to q (do later)
void add(Runnable r) {
if (r == null) ret;
synchronized { q = chainPlus(q, r); }
checkForAction();
}
// prepend to q (do next)
void addInFront(Runnable r) {
if (r == null) ret;
synchronized { q = itemPlusChain(r, q); }
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 {
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
L snapshot() { ret cloneList(q); }
// you can override this
void onIdle {}
}