| 2842 | } |
| 2843 | |
| 2844 | cbm_daemon_application_t *cbm_daemon_application_new( |
| 2845 | const cbm_daemon_application_config_t *config) { |
| 2846 | cbm_daemon_application_t *application = calloc(1, sizeof(*application)); |
| 2847 | if (!application) { |
| 2848 | return NULL; |
| 2849 | } |
| 2850 | cbm_mutex_init(&application->mutex); |
| 2851 | application->physical_job_limit = APPLICATION_DEFAULT_PHYSICAL_JOB_LIMIT; |
| 2852 | size_t aggregate_memory_budget_bytes = cbm_mem_budget(); |
| 2853 | if (config) { |
| 2854 | if ((config->ui_readiness_secret != NULL || config->ui_readiness_secret_length != 0) && |
| 2855 | (!config->ui_readiness_secret || |
| 2856 | config->ui_readiness_secret_length != sizeof(application->ui_readiness_secret))) { |
| 2857 | cbm_mutex_destroy(&application->mutex); |
| 2858 | cbm_secure_zero(application->ui_readiness_secret, |
| 2859 | sizeof(application->ui_readiness_secret)); |
| 2860 | free(application); |
| 2861 | return NULL; |
| 2862 | } |
| 2863 | application->watcher = config->watcher; |
| 2864 | application->config = config->config; |
| 2865 | application->project_locks = config->project_locks; |
| 2866 | if (config->physical_job_limit > 0) { |
| 2867 | application->physical_job_limit = config->physical_job_limit; |
| 2868 | } |
| 2869 | if (config->aggregate_memory_budget_bytes > 0) { |
| 2870 | aggregate_memory_budget_bytes = config->aggregate_memory_budget_bytes; |
| 2871 | } |
| 2872 | if (config->worker_ops) { |
| 2873 | application->worker_ops = *config->worker_ops; |
| 2874 | } |
| 2875 | if (config->update_ops) { |
| 2876 | application->update_ops = *config->update_ops; |
| 2877 | } |
| 2878 | if (config->ui_readiness_secret && |
| 2879 | config->ui_readiness_secret_length == sizeof(application->ui_readiness_secret)) { |
| 2880 | memcpy(application->ui_readiness_secret, config->ui_readiness_secret, |
| 2881 | sizeof(application->ui_readiness_secret)); |
| 2882 | application->ui_readiness_secret_set = true; |
| 2883 | } |
| 2884 | } |
| 2885 | /* Equal fixed slices keep admission deterministic: starting fewer jobs does |
| 2886 | * not let an early worker claim memory reserved for later concurrent jobs. |
| 2887 | * The absurd sub-byte-per-slot case is made safe by reducing effective |
| 2888 | * capacity before division; normal daemon budgets are many orders larger. */ |
| 2889 | if (aggregate_memory_budget_bytes > 0 && |
| 2890 | application->physical_job_limit > aggregate_memory_budget_bytes) { |
| 2891 | application->physical_job_limit = aggregate_memory_budget_bytes; |
| 2892 | } |
| 2893 | if (aggregate_memory_budget_bytes > 0 && application->physical_job_limit > 0) { |
| 2894 | application->worker_memory_budget_bytes = |
| 2895 | aggregate_memory_budget_bytes / application->physical_job_limit; |
| 2896 | } |
| 2897 | if (!application->worker_ops.start) { |
| 2898 | application->worker_ops = (cbm_daemon_application_worker_ops_t){ |
| 2899 | .context = NULL, |
| 2900 | .start = application_worker_start_default, |
| 2901 | .poll = application_worker_poll_default, |