fix(ibus): drop a context on the Service.Destroy libibus really sends

libibus destroys an input context with Destroy on
org.freedesktop.IBus.Service, not on the InputContext interface, so the
call went unanswered and the context slot stayed taken until the whole
connection closed. A long-lived GTK process that opens and closes text
widgets exhausted the 1024 slots and then got LimitsExceeded for every
new field. The live contract test now destroys a context that way and
checks the path is gone.
This commit is contained in:
2026-08-16 20:24:26 +09:00
parent f6adb117c9
commit cdd3e38195
2 changed files with 30 additions and 0 deletions

4
ibus.c
View File

@@ -854,6 +854,10 @@ onmsg(DBusConnection *c, DBusMessage *m, void*)
if(strcmp(member, "GetAll") == 0) if(strcmp(member, "GetAll") == 0)
return handlepropertygetall(c, m); return handlepropertygetall(c, m);
} }
/* libibus destroys a context through its Service base interface. */
if(strcmp(iface, "org.freedesktop.IBus.Service") == 0 &&
strcmp(member, "Destroy") == 0)
iface = "org.freedesktop.IBus.InputContext";
if(strcmp(iface, "org.freedesktop.IBus.InputContext") == 0){ if(strcmp(iface, "org.freedesktop.IBus.InputContext") == 0){
ctx = findcontext(c, path); ctx = findcontext(c, path);
if(ctx == nil) if(ctx == nil)

View File

@@ -39,6 +39,31 @@ invalidcall(DBusConnection *conn, char *path, char *member)
return ok; return ok;
} }
/* libibus destroys through the Service interface; the path must go away. */
static int
destroyed(DBusConnection *conn, char *path)
{
DBusMessage *m, *reply;
const char *name;
int ok;
m = dbus_message_new_method_call("org.freedesktop.IBus", path,
"org.freedesktop.IBus.Service", "Destroy");
reply = callret(conn, m, "Service.Destroy");
if(reply == NULL)
return 0;
dbus_message_unref(reply);
reply = sendcall(conn, contextcall(path, "FocusIn"), "FocusIn");
if(reply == NULL)
return 0;
name = dbus_message_get_error_name(reply);
ok = name != NULL && strcmp(name, DBUS_ERROR_UNKNOWN_OBJECT) == 0;
if(!ok)
fail("context survived Service.Destroy");
dbus_message_unref(reply);
return ok;
}
static int static int
overflowrejected(char *address) overflowrejected(char *address)
{ {
@@ -115,6 +140,7 @@ runcontract(char *address)
createcontext(conn, path, sizeof path, "contract context"); createcontext(conn, path, sizeof path, "contract context");
for(i = 0; ok && i < (int)(sizeof bad / sizeof bad[0]); i++) for(i = 0; ok && i < (int)(sizeof bad / sizeof bad[0]); i++)
ok = invalidcall(conn, path, bad[i]); ok = invalidcall(conn, path, bad[i]);
ok = ok && destroyed(conn, path);
closebus(&conn); closebus(&conn);
return ok; return ok;
} }