mirror of https://github.com/adamdruppe/arsd.git
this code is ugly but it can prevent deadlocks if another thread triggers a GC collection of Sprites in particular
This commit is contained in:
parent
6070dbc262
commit
017b1d9759
183
simpledisplay.d
183
simpledisplay.d
|
|
@ -1,5 +1,7 @@
|
||||||
// https://dpaste.dzfl.pl/7a77355acaec
|
// https://dpaste.dzfl.pl/7a77355acaec
|
||||||
|
|
||||||
|
// Search for: FIXME: leaks if multithreaded gc
|
||||||
|
|
||||||
// https://freedesktop.org/wiki/Specifications/XDND/
|
// https://freedesktop.org/wiki/Specifications/XDND/
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
|
// https://docs.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
|
||||||
|
|
@ -2888,6 +2890,7 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
||||||
bool _suppressDestruction;
|
bool _suppressDestruction;
|
||||||
|
|
||||||
~this() {
|
~this() {
|
||||||
|
if(!thisIsGuiThread) return; // FIXME: leaks if multithreaded gc
|
||||||
if(_suppressDestruction)
|
if(_suppressDestruction)
|
||||||
return;
|
return;
|
||||||
impl.dispose();
|
impl.dispose();
|
||||||
|
|
@ -3435,6 +3438,8 @@ private:
|
||||||
// for all windows in nativeMapping
|
// for all windows in nativeMapping
|
||||||
package static void processAllCustomEvents () {
|
package static void processAllCustomEvents () {
|
||||||
|
|
||||||
|
cleanupQueue.process();
|
||||||
|
|
||||||
justCommunication.processCustomEvents();
|
justCommunication.processCustomEvents();
|
||||||
|
|
||||||
foreach (SimpleWindow sw; SimpleWindow.nativeMapping.byValue) {
|
foreach (SimpleWindow sw; SimpleWindow.nativeMapping.byValue) {
|
||||||
|
|
@ -5076,6 +5081,7 @@ class NotificationAreaIcon : CapableOfHandlingNativeEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
~this() {
|
~this() {
|
||||||
|
if(!thisIsGuiThread) return; // FIXME: leaks if multithreaded gc
|
||||||
version(X11)
|
version(X11)
|
||||||
if(clippixmap != None)
|
if(clippixmap != None)
|
||||||
XFreePixmap(XDisplayConnection.get, clippixmap);
|
XFreePixmap(XDisplayConnection.get, clippixmap);
|
||||||
|
|
@ -5196,37 +5202,50 @@ class Timer {
|
||||||
/// Stop and destroy the timer object.
|
/// Stop and destroy the timer object.
|
||||||
void destroy() {
|
void destroy() {
|
||||||
version(Windows) {
|
version(Windows) {
|
||||||
if(handle) {
|
staticDestroy(handle);
|
||||||
// KillTimer(null, handle);
|
handle = null;
|
||||||
CancelWaitableTimer(cast(void*)handle);
|
|
||||||
mapping.remove(handle);
|
|
||||||
CloseHandle(handle);
|
|
||||||
handle = null;
|
|
||||||
}
|
|
||||||
} else version(linux) {
|
} else version(linux) {
|
||||||
if(fd != -1) {
|
staticDestroy(fd);
|
||||||
import unix = core.sys.posix.unistd;
|
fd = -1;
|
||||||
static import ep = core.sys.linux.epoll;
|
|
||||||
|
|
||||||
version(with_eventloop) {
|
|
||||||
import arsd.eventloop;
|
|
||||||
removeFileEventListeners(fd);
|
|
||||||
} else {
|
|
||||||
ep.epoll_event ev = void;
|
|
||||||
ev.events = ep.EPOLLIN;
|
|
||||||
ev.data.fd = fd;
|
|
||||||
|
|
||||||
ep.epoll_ctl(epollFd, ep.EPOLL_CTL_DEL, fd, &ev);
|
|
||||||
}
|
|
||||||
unix.close(fd);
|
|
||||||
mapping.remove(fd);
|
|
||||||
fd = -1;
|
|
||||||
}
|
|
||||||
} else featureNotImplemented();
|
} else featureNotImplemented();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(Windows)
|
||||||
|
static void staticDestroy(HANDLE handle) {
|
||||||
|
if(handle) {
|
||||||
|
// KillTimer(null, handle);
|
||||||
|
CancelWaitableTimer(cast(void*)handle);
|
||||||
|
mapping.remove(handle);
|
||||||
|
CloseHandle(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else version(linux)
|
||||||
|
static void staticDestroy(int fd) {
|
||||||
|
if(fd != -1) {
|
||||||
|
import unix = core.sys.posix.unistd;
|
||||||
|
static import ep = core.sys.linux.epoll;
|
||||||
|
|
||||||
|
version(with_eventloop) {
|
||||||
|
import arsd.eventloop;
|
||||||
|
removeFileEventListeners(fd);
|
||||||
|
} else {
|
||||||
|
ep.epoll_event ev = void;
|
||||||
|
ev.events = ep.EPOLLIN;
|
||||||
|
ev.data.fd = fd;
|
||||||
|
|
||||||
|
ep.epoll_ctl(epollFd, ep.EPOLL_CTL_DEL, fd, &ev);
|
||||||
|
}
|
||||||
|
unix.close(fd);
|
||||||
|
mapping.remove(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
~this() {
|
~this() {
|
||||||
destroy();
|
version(Windows) { if(handle)
|
||||||
|
cleanupQueue.queue!staticDestroy(handle);
|
||||||
|
} else version(linux) { if(fd != -1)
|
||||||
|
cleanupQueue.queue!staticDestroy(fd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -8525,6 +8544,7 @@ class OperatingSystemFont {
|
||||||
+/
|
+/
|
||||||
|
|
||||||
~this() {
|
~this() {
|
||||||
|
if(!thisIsGuiThread) return; // FIXME: leaks if multithreaded gc
|
||||||
unload();
|
unload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9225,27 +9245,45 @@ class Sprite : CapableOfBeingDrawnUpon {
|
||||||
/// Call this when you're ready to get rid of it
|
/// Call this when you're ready to get rid of it
|
||||||
void dispose() {
|
void dispose() {
|
||||||
version(X11) {
|
version(X11) {
|
||||||
if(xrenderPicture) {
|
staticDispose(xrenderPicture, handle);
|
||||||
XRenderFreePicture(XDisplayConnection.get, xrenderPicture);
|
xrenderPicture = None;
|
||||||
xrenderPicture = None;
|
|
||||||
}
|
|
||||||
if(handle)
|
|
||||||
XFreePixmap(XDisplayConnection.get(), handle);
|
|
||||||
handle = None;
|
handle = None;
|
||||||
} else version(Windows) {
|
} else version(Windows) {
|
||||||
if(handle)
|
staticDispose(handle);
|
||||||
DeleteObject(handle);
|
|
||||||
handle = null;
|
handle = null;
|
||||||
} else version(OSXCocoa) {
|
} else version(OSXCocoa) {
|
||||||
if(context)
|
staticDispose(context);
|
||||||
CGContextRelease(context);
|
|
||||||
context = null;
|
context = null;
|
||||||
} else static assert(0);
|
} else static assert(0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(X11)
|
||||||
|
static void staticDispose(Picture xrenderPicture, Pixmap handle) {
|
||||||
|
if(xrenderPicture)
|
||||||
|
XRenderFreePicture(XDisplayConnection.get, xrenderPicture);
|
||||||
|
if(handle)
|
||||||
|
XFreePixmap(XDisplayConnection.get(), handle);
|
||||||
|
}
|
||||||
|
else version(Windows)
|
||||||
|
static void staticDispose(HBITMAP handle) {
|
||||||
|
if(handle)
|
||||||
|
DeleteObject(handle);
|
||||||
|
}
|
||||||
|
else version(OSXCocoa)
|
||||||
|
static void staticDispose(CGContextRef context) {
|
||||||
|
if(context)
|
||||||
|
CGContextRelease(context);
|
||||||
|
}
|
||||||
|
|
||||||
~this() {
|
~this() {
|
||||||
dispose();
|
version(X11) { if(xrenderPicture || handle)
|
||||||
|
cleanupQueue.queue!staticDispose(xrenderPicture, handle);
|
||||||
|
} else version(Windows) { if(handle)
|
||||||
|
cleanupQueue.queue!staticDispose(handle);
|
||||||
|
} else version(OSXCocoa) { if(context)
|
||||||
|
cleanupQueue.queue!staticDispose(context);
|
||||||
|
} else static assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
@ -10666,6 +10704,7 @@ version(Windows) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~this() {
|
~this() {
|
||||||
|
if(!thisIsGuiThread) return; // FIXME: leaks if multithreaded gc
|
||||||
DestroyIcon(hIcon);
|
DestroyIcon(hIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -11585,8 +11624,10 @@ version(Windows) {
|
||||||
Added November 23, 2021
|
Added November 23, 2021
|
||||||
|
|
||||||
Not fully stable, may be moved out of the impl struct.
|
Not fully stable, may be moved out of the impl struct.
|
||||||
|
|
||||||
|
Default value changed to `true` on February 15, 2021
|
||||||
+/
|
+/
|
||||||
bool doLiveResizing;
|
bool doLiveResizing = true;
|
||||||
|
|
||||||
private int bmpWidth;
|
private int bmpWidth;
|
||||||
private int bmpHeight;
|
private int bmpHeight;
|
||||||
|
|
@ -21383,6 +21424,72 @@ private mixin template DynamicLoad(Iface, string library, int majorVersion, alia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/+
|
||||||
|
The GC can be called from any thread, and a lot of cleanup must be done
|
||||||
|
on the gui thread. Since the GC can interrupt any locks - including being
|
||||||
|
triggered inside a critical section - it is vital to avoid deadlocks to get
|
||||||
|
these functions called from the right place.
|
||||||
|
|
||||||
|
If the buffer overflows, things are going to get leaked. I'm kinda ok with that
|
||||||
|
right now.
|
||||||
|
|
||||||
|
The cleanup function is run when the event loop gets around to it, which is just
|
||||||
|
whenever there's something there after it has been woken up for other work. It does
|
||||||
|
NOT wake up the loop itself - can't risk doing that from inside the GC in another thread.
|
||||||
|
(Well actually it might be ok but i don't wanna mess with it right now.)
|
||||||
|
+/
|
||||||
|
private struct CleanupQueue {
|
||||||
|
import core.stdc.stdlib;
|
||||||
|
|
||||||
|
void queue(alias func, T...)(T args) {
|
||||||
|
static struct Args {
|
||||||
|
T args;
|
||||||
|
}
|
||||||
|
static struct RealJob {
|
||||||
|
Job j;
|
||||||
|
Args a;
|
||||||
|
}
|
||||||
|
static void call(Job* data) {
|
||||||
|
auto rj = cast(RealJob*) data;
|
||||||
|
func(rj.a.args);
|
||||||
|
}
|
||||||
|
|
||||||
|
RealJob* thing = cast(RealJob*) malloc(RealJob.sizeof);
|
||||||
|
thing.j.call = &call;
|
||||||
|
thing.a.args = args;
|
||||||
|
|
||||||
|
buffer[tail++] = cast(Job*) thing;
|
||||||
|
|
||||||
|
// FIXME: set overflowed
|
||||||
|
}
|
||||||
|
|
||||||
|
void process() {
|
||||||
|
const tail = this.tail;
|
||||||
|
|
||||||
|
while(tail != head) {
|
||||||
|
Job* job = cast(Job*) buffer[head++];
|
||||||
|
job.call(job);
|
||||||
|
free(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(overflowed)
|
||||||
|
throw new Exception("cleanup overflowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ubyte tail; // must ONLY be written by queue
|
||||||
|
ubyte head; // must ONLY be written by process
|
||||||
|
bool overflowed;
|
||||||
|
|
||||||
|
static struct Job {
|
||||||
|
void function(Job*) call;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*[256] buffer;
|
||||||
|
}
|
||||||
|
private __gshared CleanupQueue cleanupQueue;
|
||||||
|
|
||||||
void guiAbortProcess(string msg) {
|
void guiAbortProcess(string msg) {
|
||||||
import core.stdc.stdlib;
|
import core.stdc.stdlib;
|
||||||
version(Windows) {
|
version(Windows) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue