Skip to content(if available)orjump to list(if available)

Emacs: The macOS Bug

Emacs: The macOS Bug

88 comments

·July 30, 2025

rayiner

This is a problem with Emacs on virtually every GUI platform. Emacs insists that it owns the main loop, while most GUI frameworks insist that they own the main loop. Emacs wants to slurp some events from a queue-like thing, throw some drawing at another queue-like thing, then wait for another event. The GUI instead wants to call back into Emacs whenever an event comes in.

All that being said, Emacs has always worked pretty well for me on Mac. I use Emacs and PDFgrep to spelunk through multi-GBs of PDFs and it is faster than almost anything else.

AceJohnny2

As Daniel Colascione ('quotemstr' around these parts) said [1]:

> GNU Emacs is an old-school C program emulating a 1980s Symbolics Lisp Machine emulating an old-fashioned Motif-style Xt toolkit emulating a 1970s text terminal emulating a 1960s teletype. Compiling Emacs is a challenge. Adding modern rendering features to the redisplay engine is a miracle.

Emacs owns its main loop because, damnit, it created one before it was cool.

Hats off to any heroes who would manage to drag it, kicking and screaming, into this millennium.

[1] https://gist.github.com/ghosty141/c93f21d6cd476417d4a9814eb7...

krackers

I'm not sure if I understood the issue right, but if the issue is with the runloop, I think you can technically reimplement it yourself with nextEventMatchingMask.

But their design just seems broken, if they're re-initializing the graphics context on every runloop iteration?

rayiner

That’s what nsterm.m uses. As far as I can tell, the problem is that nextEventMatchingMask needs to be called from the NSApp main loop. But calling NSApplication:run blocks and takes over the main loop, which doesn’t work with for Emacs.

So what Emacs does is keep control of its own main loop. It has a select() that listens for events, and then calls NSApplication:run in the event handler. Emacs’s implementation of run() processes all pending events and then exits and returns control to the real Emacs main loop. So every keystroke or timer event in Emacs invokes setting up and tearing down the entire NSApplication main loop.

The relevant code is ns_select_1 in nsterm.m, line 5102. https://github.com/emacs-mirror/emacs/blob/master/src/nsterm...

krackers

Hm I'm still not sure I understand what you mean by "sets up and tears down the entire main loop". Even in normal cocoa applications every keystroke or input event causes an event to be dispatched from WindowServer to the application which gets handled by the runloop. The expectation of course is that you never do any blocking work on this runloop. nextEventMatchingMask doesn't have to block, you can have it return if there are no events that need to be processed so it can integrate with your own runloop (of course bypassing [NSApplication run] and implementing the runloop yourself this way is discouraged, but I don't think it's forbidden)

The model of "slurp some events from a queue-like thing, throw some drawing at another queue-like thing, then wait for another event" is precisely what [NSApplication run] already implements. Per Apple

>A Cocoa application is event driven: It fetches an event from the queue, dispatches it to an appropriate object, and, after the event is handled, fetches the next event. With some exceptions (such as modal event loops) an application continues in this pattern until the user quits it.

null

[deleted]

rahen

This is worth undertaking. macOS's stricter approach to handling some questionable hacks in Emacs could improve the codebase across all platforms. The PGTK frontend for Emacs (the Wayland-native frontend) was derived from the macOS version for instance. It replaced much of the messy X11 code with a cleaner, more modular Cairo-based frontend, which could be further enhanced by adopting a cross-platform, more future-proof SDL toolkit.

https://appetrosyan.github.io/posts/emacs-widget

Hopefully, similar improvements can address the issues with large locks and the lack of proper threading.

tsujp

The problem with the PGTK frontend is it is notoriously EXTREMELY slow. The latency on user input compared to the X11 (especially Lucid) version has some people reverting back to X11/Lucid.

When I do run Linux I run Wayland, I daily drive macOS, but better than both are what you already allude to: the Emacs widget toolkit which will focus on replacing the GUI frontend with SDL and also (equally potentially) introducing an actor-type framework (akin to BEAM's) for communication to decouple that GUI.

fiddlerwoaroof

I wonder how hard it would be to run the pgtk version on macOS

flylikeabanana

I run emacs on MacOS specifying the `emacs-pgtk` build in my Nix config as the package. Seems to work quite well for me

dwb

Kind of wish I had the time/energy to help out here cos I'm motivated and interested, but I don't, so I'll just say thanks for the effort and write-up! The emacs-devel thread is interesting too: https://lists.gnu.org/archive/html/emacs-devel/2025-07/msg00...

kkylin

I recently noticed deleting a frame doesn't seem to free the associated memory; you can see this by running Activity Monitor and opening and deleting some frames. It's on the order of 10s of MB (exact amount depends on size of frame, whether it is fullscreen, etc). This is not much on a modern machine, but if you open and close lots of frames everyday (as I do) and keep Emacs running for weeks at a time (I do that too) then it starts to add up. My current kludge is to add a hook to resize a frame (which deallocate most of the frame memory) before deleting. This keeps the leaked memory to a level that is more tolerable.

(I've dug through the ObjC source, specifically "nsterm.m", but haven't quite figured out the core problem.)

[edited slightly for clarity]

mattlangston

fwiw I live in [macOS emacs](https://emacsformacosx.com/) all day long for systems engineering (C/C++) and have 201 open buffers, an uptime of 57 days and ~540 MB memory usage.

e40

I used to use that version of emacs, but performance issues on my Mac Studio made using it just untenable. I switched to Homebrew's "emacs-plus" which does not suffer, for whatever reason, the same performance issue. Based on TFA, I'm somewhat baffled as to why, but I can't argue with results.

eviks

> It sounds horrible. But does it make sense? If it’s that bad - why didn’t anyone notice?

But they did! As you say yourself

> For many, this slowness won’t be a surprise. There are plenty of complaints about slowness on MacOS, especially around popular packages.

So this is some parody, describing as efficient something rather inefficient

> Because Emacs is very efficient.

> For instance, dragging a window handle - depending on the machine - could result in thousands if not millions of such events, causing allocation and reallocation of gigabytes of memory;

lygaret

there's another interesting issue on OSX which I've got a patch for in my homebrew version: on OS X, sleeping in a thread can hang, which causes LSP issues for me (through the `lsp-auto-install` package, since it downloads in a thread). The bug thread is interesting, but seems to have petered out; the patch works for me though!

[bug]: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=75275#83 [brew]: https://github.com/d12frosted/homebrew-emacs-plus/compare/ma...

semi-extrinsic

Another example, if you run gunicorn with --preload on Macos, you need to export OBJC_DISABLE_INITIALIZE_FORK_SAFETY and some shenanigans.

vim-guru

On macOS and Linux, I haven't noticed any performance issues with Emacs. On Windows, however, the performance is significantly worse. To make matters worse, I even have to patch w32.c just to get it to build:

@@ -10298,7 +10298,7 @@ w32_read_registry (HKEY rootkey, Lisp_Object lkey, Lisp_Object lname) /* mingw.org's MinGW doesn't declare _dstbias. MinGW64 defines it as a macro. / #ifndef _dstbias -__MINGW_IMPORT int _dstbias; +__MINGW_IMPORT long _dstbias; #endif

/ Fix a bug in MS implementation of 'tzset'. This function should be

null

[deleted]

bitwize

I do not seem to be experiencing this issue on my work machine, which is a 64 GiB MBP, running Homebrew's version of Emacs. It runs fine, but the endpoint security software activates every single time a binary is run, which means that Emacs workflows heavy on spawning subprocesses, like magit, tend to be slow.

hollerith

And if someone manages to fix the bug described in the OP, he might have to maintain it as a fork because some influential emacs maintainers want it to be frustrating and unpleasant to use Emacs on non-Free OSes.

uludag

I think this is a uncharitable take. I don't feel at all that the maintainers have this level of disdain for non-free OSes. Just type C-h n and you can see work done for non-free OSes (e.g. "'NSSpeechRecognitionUsageDescription' now included in "Info.plist" (macOS).")

I don't think there'd be pushback on bug fixes. I think it's only new features that would only exist on macOS that get pushback.

goranmoomin

As someone who have tried upstreaming a patch for Emacs on macOS (to add a feature that already existed on Linux), I can bitterly say that at least some of the maintainers do have a disdain for non-free OSes and that it makes it contributing patches for macOS as miserable as possible.

The patch was adding xwidget webkit support for macOS Cocoa[0], which I iterated for the next few months[1], only to side-rail into a discussion on macOS/GCC and GNUStep support policy[2], and I fizzled out.

That was abt 5 years ago, and I’ve never touched on the Emacs codebase since.

[0]: https://lists.gnu.org/archive/html/emacs-devel/2019-05/msg00...

[1]: https://lists.gnu.org/archive/html/emacs-devel/2019-07/threa...

[2]: https://lists.gnu.org/archive/html/emacs-devel/2019-08/msg00...

lvass

>I think it's only new features that would only exist on macOS that get pushback

Not even that anymore, it seems. https://xenodium.com/emacs-send-to-aka-macos-sharing-merged-...

rpdillon

I mean, pretty much exactly that.

> If you're wondering what was controversial about the patch, GNU guidelines discourage adding features targeting non-free operating systems before it can be made available for GNU/Linux. While the patch could be easily reworked to expose the native capabilities available for each platform, there's plenty of room for interpretation as to whether a rework is considered enough to satisfy the guideline. Most of the discussion was centered around this topic. Once the thread was refocused around shaping the patch, I received super constructive feedback and the patch was indeed reworked to cater for different platforms. We also agreed to rename the feature from "share" to "send". To my surprise, even RMS also chimed in on the patch discussion. Achievement unlocked?!

hollerith

I used Emacs on OS X (for 10 years, ending 4.5 y ago). Have you?

uludag

Almost actually. My nine year anniversary is coming up this November. Obviously the experience isn't perfect (like the article mentions), but it's perfectly usable.

You don't have to look hard to see that the Emacs maintainers aren't actively hostile to non-free OSes. Android is another good example. The Emacs manual states "it must be necessary to consider Android proprietary software from a practical standpoint." and yet a good amount of work went in to adding support for Android.

jrockway

I have. Emacs has never felt actively hostile to me. Rather (as I describe above), running Emacs with a window system has always felt a little jank... free OS or otherwise. (Honestly, Windows is where I've had the best experience.)

PaulDavisThe1st

Aquamacs, which IME is the best GNU Emacs for macOS, is already a fork.

deafpolygon

that kind of inflexible ideology is one of the reasons why i avoid emacs in general

exe34

Tbh I'd avoid MacOS if I can't run emacs on it.

Jtsummers

I've used emacs nearly daily, most of that time with emacs --daemon and computer uptimes measured in months, on macOS (nee OS X) for nearly 20 years. It works fine. I've never encountered whatever issue this post is about in all that time. I cannot think of an instance when emacs froze up on me.

f1shy

The funny thing is I bought a mac some years ago because Emacs was installed by default. Sadly does bot come anymore installed, probably because of bad support

worik

"inflexible ideology" is the dominant paradigm for Apple is it not?

Mēh! Maybe not "inflexible", more "inscrutable", very hard to discern any method to their madness....

quotemstr

> Even in the best case though, things won’t be as great as they are on Linux or Windows.

Worst case, you just swap out the NS p pselect hack and use a w32-like separate thread. Let Emacs be Emacs and let Cocoa be Cocoa.

NS would be just as good as Windows then. Isn't that bood enough? Maybe this thread splitting is the "deep event loop surgery" the author meant? I haven't been following.