| 656 | } |
| 657 | |
| 658 | bool cbm_daemon_watch_unsubscribe(cbm_daemon_coordinator_t *coordinator, |
| 659 | cbm_daemon_client_id_t client_id, |
| 660 | cbm_daemon_subscription_id_t subscription_id) { |
| 661 | if (!coordinator || client_id == CBM_DAEMON_CLIENT_ID_INVALID || |
| 662 | subscription_id == CBM_DAEMON_SUBSCRIPTION_ID_INVALID) { |
| 663 | return false; |
| 664 | } |
| 665 | |
| 666 | cbm_daemon_callback_batch_t batch; |
| 667 | cbm_mutex_lock(&coordinator->mutex); |
| 668 | callback_batch_init_locked(coordinator, &batch); |
| 669 | if (!find_client_locked(coordinator, client_id)) { |
| 670 | cbm_mutex_unlock(&coordinator->mutex); |
| 671 | return false; |
| 672 | } |
| 673 | bool removed = false; |
| 674 | cbm_daemon_watch_t **cursor = &coordinator->watches; |
| 675 | while (*cursor) { |
| 676 | cbm_daemon_watch_t *watch = *cursor; |
| 677 | removed = remove_subscription_locked(&watch->subscriptions, &watch->subscription_count, |
| 678 | client_id, subscription_id); |
| 679 | if (!removed) { |
| 680 | cursor = &watch->next; |
| 681 | continue; |
| 682 | } |
| 683 | if (watch->subscription_count == 0) { |
| 684 | *cursor = watch->next; |
| 685 | watch->next = NULL; |
| 686 | coordinator->watch_count--; |
| 687 | queue_watch_release_locked(coordinator, watch, &batch); |
| 688 | } |
| 689 | break; |
| 690 | } |
| 691 | cbm_mutex_unlock(&coordinator->mutex); |
| 692 | callback_batch_run(coordinator, &batch); |
| 693 | return removed; |
| 694 | } |
| 695 | |
| 696 | size_t cbm_daemon_job_subscribers(cbm_daemon_coordinator_t *coordinator, const char *project_key) { |
| 697 | if (!coordinator || !project_key) { |
no test coverage detected