comparison seobeo/s_sse.c @ 264:04fee26ecce0

add authenticated JRPG conversation platform Add reusable auth/session storage, owned conversation recovery, guest quotas, admin workflows, URL-routed conversation UI, mobile frame support, and parallel browser acceptance. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Fri, 07 Aug 2026 07:34:12 -0700
parents 609d3c6aff4e
children
comparison
equal deleted inserted replaced
263:ee04e4e69fed 264:04fee26ecce0
405 Dowa_Array_Push(g_sse_streams, p_stream); 405 Dowa_Array_Push(g_sse_streams, p_stream);
406 pthread_mutex_unlock(&g_sse_mutex); 406 pthread_mutex_unlock(&g_sse_mutex);
407 return p_stream; 407 return p_stream;
408 } 408 }
409 409
410 void Seobeo_SSE_Set_Detach_Callback(
411 Seobeo_SSE_Stream *p_stream,
412 void (*cb)(Seobeo_SSE_Stream *, void *),
413 void *ctx)
414 {
415 if (!p_stream)
416 return;
417 pthread_mutex_lock(&g_sse_mutex);
418 p_stream->detach_cb = cb;
419 p_stream->detach_ctx = ctx;
420 pthread_mutex_unlock(&g_sse_mutex);
421 }
422
410 void Seobeo_SSE_Server_Detach_Handle(Seobeo_Handle *p_handle) 423 void Seobeo_SSE_Server_Detach_Handle(Seobeo_Handle *p_handle)
411 { 424 {
412 if (!p_handle) 425 if (!p_handle)
413 return; 426 return;
414 p_handle->is_sse = FALSE; 427 p_handle->is_sse = FALSE;
428
429 /* Save callback info before releasing the stream reference. */
430 void (*detach_cb)(Seobeo_SSE_Stream *, void *) = NULL;
431 void *detach_ctx = NULL;
432 Seobeo_SSE_Stream *cb_stream = NULL;
433
415 pthread_mutex_lock(&g_sse_mutex); 434 pthread_mutex_lock(&g_sse_mutex);
416 size_t count = Dowa_Array_Length(g_sse_streams); 435 size_t count = Dowa_Array_Length(g_sse_streams);
417 for (size_t i = 0; i < count; i++) 436 for (size_t i = 0; i < count; i++)
418 { 437 {
419 Seobeo_SSE_Stream *p_stream = g_sse_streams[i]; 438 Seobeo_SSE_Stream *p_stream = g_sse_streams[i];
420 if (p_stream->p_handle != p_handle) 439 if (p_stream->p_handle != p_handle)
421 continue; 440 continue;
422 p_stream->closed = TRUE; 441 p_stream->closed = TRUE;
423 p_stream->p_handle = NULL; 442 p_stream->p_handle = NULL;
424 sse_clear_pending_unlocked(p_stream); 443 sse_clear_pending_unlocked(p_stream);
444 detach_cb = p_stream->detach_cb;
445 detach_ctx = p_stream->detach_ctx;
446 /* Keep a live reference for the callback; release afterward. */
447 cb_stream = p_stream;
425 g_sse_streams[i] = Dowa_Array_Pop(g_sse_streams); 448 g_sse_streams[i] = Dowa_Array_Pop(g_sse_streams);
426 sse_release_unlocked(p_stream); 449 /* Do NOT call sse_release_unlocked here; do it after mutex released
450 * so the callback can safely acquire its own locks. */
427 break; 451 break;
428 } 452 }
429 pthread_mutex_unlock(&g_sse_mutex); 453 pthread_mutex_unlock(&g_sse_mutex);
454
455 /* Fire callback outside the mutex so callers may acquire other locks. */
456 if (detach_cb && cb_stream)
457 detach_cb(cb_stream, detach_ctx);
458
459 /* Drop the server's reference (the callback may hold its own reference). */
460 if (cb_stream)
461 Seobeo_SSE_Release(cb_stream);
430 } 462 }
431 463
432 void Seobeo_SSE_Server_Destroy(void) 464 void Seobeo_SSE_Server_Destroy(void)
433 { 465 {
434 pthread_mutex_lock(&g_sse_mutex); 466 pthread_mutex_lock(&g_sse_mutex);