Skip to content

Refactor CH thread management#594

Open
Roytak wants to merge 4 commits intodevelfrom
ch-lock-fix
Open

Refactor CH thread management#594
Roytak wants to merge 4 commits intodevelfrom
ch-lock-fix

Conversation

@Roytak
Copy link
Copy Markdown
Collaborator

@Roytak Roytak commented Apr 13, 2026

Change CH threads from detached to joinable (there was a memory leak where there wasn't enough time to free the thread when the server is stopping). The server now joins all the CH threads before ending.

Also fix multiple instances of incorrectly checking the return value of nc_rwlock_lock().

Added a function nc_session_server_ch_client_dispatch_stop_if_dispatched() that joins these CH threads on config change or server destroy.

Added a WRN log to inform the user that even though he created new CH clients, they won't be dispatched because required CBs were not set, previously this was done silently and threads of clients that were deleted weren't being stopped because of that.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors Call Home (CH) thread lifecycle management to avoid shutdown-time leaks by switching from detached threads to joinable threads and introducing a centralized “stop if dispatched” helper used during config changes and server teardown. It also tightens lock/return-value handling in several CH-related paths and adds a warning when CH clients cannot be dispatched due to missing callbacks.

Changes:

  • Make CH client threads joinable and introduce nc_session_server_ch_client_dispatch_stop_if_dispatched() to stop+join them during config updates and nc_server_destroy().
  • Improve Call Home reconcile behavior (stop deleted clients even if dispatch callbacks are missing) and add a WRN log when new CH clients won’t be dispatched.
  • Minor session transport/logging improvements and library version bump.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/session.c Adjusts CH lock handling during session free and improves an SSH EOF timeout warning message.
src/session_server.h Restructures NC_ENABLED_SSH_TLS preprocessor guards around SSH-related declarations.
src/session_server.c Implements CH thread stop+join helper, switches CH threads to joinable, and updates server destroy teardown ordering.
src/session_p.h Adds CH thread pthread_t storage and declares the new internal stop helper.
src/server_config.c Updates CH client reconciliation to use the centralized stop helper and adds a warning when dispatch callbacks are missing.
CMakeLists.txt Bumps micro version / soversion.
Comments suppressed due to low confidence (1)

src/session_server.c:3864

  • With threads now joinable and stoppable via thread_running, there is a race where a stop can be requested before the new thread executes and sets data->thread_running = 1 (the thread currently sets it unconditionally on startup). In that case, the stop request can be overwritten and pthread_join() in the stop helper may block indefinitely. Consider initializing arg->thread_running under arg->cond_lock before pthread_create() and updating the thread start logic to not flip it back to running if a stop was already requested.
    pthread_cond_init(&arg->cond, NULL);
    pthread_mutex_init(&arg->cond_lock, NULL);

    /* CH THREADS WR LOCK */
    if (nc_rwlock_lock(&server_opts.ch_threads_lock, NC_RWLOCK_WRITE, NC_CH_THREADS_LOCK_TIMEOUT, __func__) != 1) {
        rc = -1;
        goto cleanup;
    }
    ch_threads_lock = NC_RWLOCK_WRITE;

    /* Append the thread data pointer to the global array.
     * Pointer instead of struct so that server_opts.ch_thread_lock does not have to be
     * locked everytime the arg is accessed */
    LY_ARRAY_NEW_GOTO(NULL, server_opts.ch_threads, new_item, rc, cleanup);
    *new_item = arg;

    /* create the CH thread */
    if ((r = pthread_create(&arg->tid, NULL, nc_ch_client_thread, arg))) {
        ERR(NULL, "Creating a new thread failed (%s).", strerror(r));
        /* remove from the array */
        LY_ARRAY_DECREMENT_FREE(server_opts.ch_threads);
        rc = -1;
        goto cleanup;
    }

    /* arg is now owned by the thread */
    arg = NULL;


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Roytak added 4 commits April 13, 2026 16:50
Store thread IDs in ch_thread_arg to allow proper pthread_join when
stopping threads. Add nc_session_server_ch_client_dispatch_stop_if_dispatched()
to centralize thread stopping logic, fixing several locking issues:
- Only unlock ch_lock if it was successfully acquired in nc_session_free()
- Use config_update_lock to prevent data race in nc_server_destroy()
- Properly handle config lock unlocking during thread join to avoid deadlock
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants