A little genZ gig economy, a little Lovecraft, and a little trans-genomics
Some initial character drafts
Things I've found on the way to Getting There
A little genZ gig economy, a little Lovecraft, and a little trans-genomics
Some initial character drafts
import { useCallback, useState, useEffect, useRef } from "react";
// Automatically scroll to the bottom of the container when the size or
// scrolling content changes, unless the user has started scrolling.
// Set onUserScroll to false to always scroll to bottom on component size
// changes, regardless of user activity.
export const useAutoScroll = (onUserScroll = null) => {
const [container, setContainer] = useState(null);
const ref = useCallback(node => setContainer(node), []);
const tailRef = useRef();
const isUserScrolling = useRef();
const isAutoScrolling = useRef();
const lastScrollHeight = useRef();
const atBottomScrollTop = useRef();
const isAtBottom = el => Math.abs(el.scrollTop + el.clientHeight - el.scrollHeight) < 1;
const scrollToBottom = (onComplete = null) => {
if (!container || isAtBottom(container)) {
return;
}
isAutoScrolling.current = true;
container.classList.add('auto-scrolling');
// Check if any ancestor container is user-scrolling or auto-scrolling
// If so, we need to avoid scrollIntoView which would scroll the ancestor
const hasScrollingAncestor = container.parentElement?.closest('.user-scrolling, .auto-scrolling') !== null;
// Use scrollIntoView only if no ancestor is scrolling (nice smooth scroll)
// Otherwise use scrollTop to avoid triggering ancestor scroll and causing loops
if (hasScrollingAncestor) {
// Only scroll this container, don't trigger ancestor scrolling
container.scrollTop = container.scrollHeight;
// Immediately mark as complete since scrollTop is synchronous
atBottomScrollTop.current = container.scrollTop;
isAutoScrolling.current = false;
container.classList.remove('auto-scrolling');
onComplete && onComplete(container);
} else {
tailRef.current?.scrollIntoView({ behavior: "smooth" });
// Check to see if we're at the bottom yet
setTimeout(() => {
if (container) {
if (!isAtBottom(container)) {
scrollToBottom(onComplete);
} else {
atBottomScrollTop.current = container.scrollTop;
isAutoScrolling.current = false;
container.classList.remove('auto-scrolling');
onComplete && onComplete(container);
}
}
}, 50);
}
};
const autoScroll = (force = false, onComplete = null) => {
if (!isUserScrolling.current || force) {
scrollToBottom(onComplete);
}
else {
onComplete && onComplete(container);
}
};
const scrollHandler = e => {
if (e.target !== container) {
return;
}
const hasScrollbar = container.scrollHeight > container.clientHeight;
lastScrollHeight.current = container.scrollHeight;
if (atBottomScrollTop.current === null) {
atBottomScrollTop.current = container.scrollHeight - container.clientHeight;
}
const currentScrollTop = container.scrollTop;
// Clear user scrolling if at bottom or no scrollbar
if (isAtBottom(container) || !hasScrollbar) {
atBottomScrollTop.current = currentScrollTop;
if (isUserScrolling.current) {
isUserScrolling.current = false;
container.classList.remove('user-scrolling');
}
}
// Detect user scrolling up
else if (!isAutoScrolling.current
&& atBottomScrollTop.current != null
&& atBottomScrollTop.current > currentScrollTop) {
if (!isUserScrolling.current) {
isUserScrolling.current = true;
container.classList.add('user-scrolling');
onUserScroll && onUserScroll(e);
}
}
};
// Ensure we trigger autoscroll on component content/size change
useEffect(() => {
if (container) {
lastScrollHeight.current = container.scrollHeight;
container.addEventListener("scroll", scrollHandler);
const mutationObserver = new MutationObserver((mutations) => {
// If content was cleared or significantly reduced, reset user scrolling flag
const contentCleared = container.scrollHeight <= container.clientHeight;
if (contentCleared) {
isUserScrolling.current = false;
container.classList.remove('user-scrolling');
}
autoScroll();
});
mutationObserver.observe(container, {childList: true, subtree: true, characterData: true});
let resizeTimeout;
const resizeObserver = new ResizeObserver((entries) => {
// Debounce to avoid "ResizeObserver loop completed with undelivered notifications"
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
// Clear user scrolling if scrollbar disappears
const hasScrollbar = container.scrollHeight > container.clientHeight;
if (!hasScrollbar && isUserScrolling.current) {
isUserScrolling.current = false;
container.classList.remove('user-scrolling');
}
autoScroll();
}, 0);
});
Array.from(container.children).forEach(child => resizeObserver.observe(child));
resizeObserver.observe(container);
// Initialize state
autoScroll();
return () => {
container.removeEventListener("scroll", scrollHandler);
mutationObserver.disconnect();
resizeObserver.disconnect();
};
}
}, [container, onUserScroll]);
return [ref, tailRef, autoScroll];
};
class TQDMBytesReader(object):
"""Show progress while reading from a file"""
def __init__(self, fd, **kwargs):
self.fd = fd
from tqdm import tqdm
self.tqdm = tqdm(**kwargs)
def read(self, size=-1):
bytes = self.fd.read(size)
self.tqdm.update(len(bytes))
return bytes
def readline(self):
bytes = self.fd.readline()
self.tqdm.update(len(bytes))
return bytes
def __enter__(self):
self.tqdm.__enter__()
return self
def __exit__(self, *args, **kwargs):
return self.tqdm.__exit__(*args, **kwargs)
with open(filename, "rb") as fd, \
TQDMBytesReader(fd, desc=f"Loading 'pickle", unit="b",
total=os.path.getsize(filename) as reader:
obj = pickle.load(reader)
class TQDMBytesWriter(object):
"""Show progress while writing to a file"""
def __init__(self, fd, **kwargs):
self.fd = fd
from tqdm import tqdm
self.tqdm = tqdm(**kwargs)
def write(self, b):
bytes_written = self.fd.write(b)
self.tqdm.update(bytes_written or 0)
return bytes_written
def __enter__(self):
self.tqdm.__enter__()
return self
def __exit__(self, *args, **kwargs):
return self.tqdm.__exit__(*args, **kwargs)
def cprofile_wsgi_middleware(app): """ Call this middleware hook to enable cProfile on each request. Statistics are dumped to the log at the end of the request. :param app: WSGI app object :return: WSGI middleware wrapper """ def _cprofile_wsgi_wrapper(environ, start_response): import cProfile, cStringIO, pstats profile = cProfile.Profile() try: return profile.runcall(app, environ, start_response) finally: stream = cStringIO.StringIO() stats = pstats.Stats(profile, stream=stream) stats.strip_dirs().sort_stats('cumulative', 'time', 'calls').print_stats(25) logging.info('cProfile data:\n%s', stream.getvalue()) return _cprofile_wsgi_wrapper def webapp_add_wsgi_middleware(app): return cprofile_wsgi_middleware(app)
Easy enough. Note that TlsSetValue is called elsewhere only when the callback decides it doesn't want to detach immediately (rather than every time a native thread attaches). Callbacks normally detach on exit if they weren't attached to begin with.// Store thread-local information required to detach the thread // (in my case, only a JVM reference was required) // TlsSetValue is only set when we recognize that we need the // extra cleanup static DWORD dwTlsIndex; BOOL WINAPI DllMain(HINSTANCE hDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: dwTlsIndex = TlsAlloc(); if (dwTlsIndex == TLS_OUT_OF_INDEXES) { return FALSE; } break; case DLL_PROCESS_DETACH: TlsFree(dwTlsIndex); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: { extern void detach_thread(); detach_thread(TlsGetValue(dwTlsIndex)); break; } default: break; } return TRUE; }
// Basic plumbing to create a unique key identifying
// specific thread-local storage
static pthread_key_t key;
static void make_key() {
extern void detach_thread();
pthread_key_create(&key, detach_thread);
}
...
// This code gets called whenever we identify that
// a thread needs detach-on-exit behavior
static pthread_once_t key_once = PTHREAD_ONCE_INIT;
pthread_once(&key_once, make_key);
if (!jvm || pthread_getspecific(key) == NULL) {
pthread_setspecific(key, jvm);
}
...
<nativelib> tag, JNLP requires the <all-permission> tag, which seems to re-trigger the requirement for signed code. Self-signing the code is no big deal, but that triggers web-starts authorization dialogs to allow the unknown CA to be used. Fortunately, the somewhat obscure deployment properties file can be used to temporarily allow the self-signed certs and bypass the dialogs. The tests can be run entirely without user input (except in the case of *nix, where NetX is not sufficiently configurable -- you have to dismiss the dialog on the first test run).
class Big extends Structure {
public int toNative;
public int fromNative;
// plus lots more fields
// the more, the bigger the difference in performance
}
class FastBig extends Big {
public void read() { }
public void write() { }
}
Big big = new Big();
big.toNative = 42;
lib.callMyNativeFunction(big);
System.out.println("Got " + big.fromNative);
Big fast = new FastBig();
fast.toNative = 42;
fast.writeField("toNative");
lib.callMyNativeFunction(fast);
System.out.println("Got " + fast.readField("fromNative"));
% windres -i java.dll -O rc
// Type: version
// Name: 1
LANGUAGE 0, 0
1 VERSIONINFO
FILEVERSION 6, 0, 30, 5
PRODUCTVERSION 6, 0, 30, 5
FILEFLAGSMASK 0x3f
FILEOS 0x4
FILETYPE 0x2
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004b0"
BEGIN
VALUE "CompanyName", "Sun Microsystems, Inc."
VALUE "FileDescription", "Java(TM) Platform SE binary"
VALUE "FileVersion", "6.0.30.5"
VALUE "Full Version", "1.6.0_03-b05"
VALUE "InternalName", "java"
VALUE "LegalCopyright", "Copyright \251 2004"
VALUE "OriginalFilename", "java.dll"
VALUE "ProductName", "Java(TM) Platform SE 6 U3"
VALUE "ProductVersion", "6.0.30.5"
END
END
public class Pointer {
public static final int SIZE = Native.POINTER_SIZE;
}
public class Native {
public static final int POINTER_SIZE;
static {
initIDs();
POINTER_SIZE = pointerSize();
}
private static native int pointerSize();
}
There errors were all caused by Pointer.SIZE having a zero value. I figured that since Pointer.SIZE was final, the value it was assigned from must also have been zero. A quick inspection on Native.POINTER_SIZE, however, showed that it was propertly initialized and non-zero. How, then, could Pointer.SIZE be zero if Native.POINTER_SIZE was not?
Anyone who has done any JNI is probably aware that Java access modifiers don't mean anything to native code. My first thought was that perhaps "final" modifiers don't mean anything either. Only problem is that I was never touching that Pointer.SIZE field in native code. Never even accessed it.
Scanning though the native initialization code, I noticed that, among a host of standard JRE classes, a reference to the Pointer class was being created. This turns out to be the cause of my problem: during Native class initialization, the Pointer class was loaded from JNI. When the pointer final field was initialized, it got the value from a not-yet-initialized Native class.
Turns out, this can be duplicated with pure Java code:
public class LoadTest {
public static final int VALUE;
static {
System.out.println("value=" + Secondary.VALUE);
VALUE = 1;
System.out.println("value=" + Secondary.VALUE);
}
}
class Secondary {
public static final int VALUE = LoadTest.VALUE;
}
Maybe I'll call this antipattern something curiously obscure, like "Law of the Polygamist's Second Wife".