Skip to content

close() frees the client while a command is in-flight #284

Description

@Aryex

Summary

close() can free the native client handle while another thread is mid-dispatch in Bindings.command / Bindings.batch / Bindings.invoke_script. Those FFI calls are blocking: true (they release the GVL and run in native code), so a concurrent close calling Bindings.close_client tears down the connection under a live dispatch → intermittent malloc(): unaligned tcache chunk detected SIGABRT.

This is distinct from the close-vs-close double-free already fixed in #224. That fix serializes close against close; it does not serialize close against an in-flight command.

Repro

require "valkey-glide"

client = Valkey.new
workers = 20.times.map do
  Thread.new { loop { client.get("k") rescue break } }
end

sleep 0.05          # let GETs start dispatching into native code
client.close        # frees the handle while commands are still in-flight
workers.each(&:join)
# Intermittent: `malloc(): unaligned tcache chunk detected`, SIGABRT.
# Run in a loop to reproduce reliably:
#   while ruby repro.rb; do :; done

invoke_script reproduces the same way (swap client.get("k") for an EVAL).

Expected

close() should either wait for in-flight commands to finish or make them fail fast — it must never free the handle while a native dispatch is live. After close, further commands should raise ConnectionError("the client is closed"), not crash.

Notes

  • Ruby-side fix proposed in fix: drain in-flight commands before close frees the handle #283 (drain in-flight commands under @close_lock before freeing; try_lock phase keeps Signal.trap shutdown working).
  • The same race exists in the shared FFI stack — a close_client-drains-in-flight guarantee in libglide_ffi would be the complete cross-client fix.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions