/* * Forget that a dynamic shmem segment is owned by a ResourceOwner */ void ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg) { if (!ResourceArrayRemove(&(owner->dsmarr), PointerGetDatum(seg))) elog(ERROR, "dynamic shared memory segment %u is not owned by resource owner %s", dsm_segment_handle(seg), owner->name); }
/* * Debugging subroutine */ static void PrintDSMLeakWarning(dsm_segment *seg) { elog(WARNING, "dynamic shared memory leak: segment %u still referenced", dsm_segment_handle(seg)); }
/* * Initialize dsm segment. Returns true if new segment was created and * false if attached to existing segment */ bool init_dsm_segment(size_t blocks_count, size_t block_size) { bool ret; /* if there is already an existing segment then attach to it */ if (dsm_cfg->segment_handle != 0) { ret = false; segment = dsm_attach(dsm_cfg->segment_handle); } /* * If segment hasn't been created yet or has already been destroyed * (it happens when last session detaches segment) then create new one */ if (dsm_cfg->segment_handle == 0 || segment == NULL) { /* create segment */ segment = dsm_create(block_size * blocks_count, 0); dsm_cfg->segment_handle = dsm_segment_handle(segment); dsm_cfg->first_free = 0; dsm_cfg->block_size = block_size; dsm_cfg->blocks_count = blocks_count; init_dsm_table(block_size, 0, dsm_cfg->blocks_count); ret = true; } /* * Keep mapping till the end of the session. Otherwise it would be * destroyed by the end of transaction */ dsm_pin_mapping(segment); return ret; }
/* * Forget that a dynamic shmem segment is owned by a ResourceOwner */ void ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg) { dsm_segment **dsms = owner->dsms; int ns1 = owner->ndsms - 1; int i; for (i = ns1; i >= 0; i--) { if (dsms[i] == seg) { while (i < ns1) { dsms[i] = dsms[i + 1]; i++; } owner->ndsms = ns1; return; } } elog(ERROR, "dynamic shared memory segment %u is not owned by resource owner %s", dsm_segment_handle(seg), owner->name); }
/* * Register background workers. */ static worker_state * setup_background_workers(int nworkers, dsm_segment *seg) { MemoryContext oldcontext; BackgroundWorker worker; worker_state *wstate; int i; /* * We need the worker_state object and the background worker handles to * which it points to be allocated in CurTransactionContext rather than * ExprContext; otherwise, they'll be destroyed before the on_dsm_detach * hooks run. */ oldcontext = MemoryContextSwitchTo(CurTransactionContext); /* Create worker state object. */ wstate = MemoryContextAlloc(TopTransactionContext, offsetof(worker_state, handle) + sizeof(BackgroundWorkerHandle *) * nworkers); wstate->nworkers = 0; /* * Arrange to kill all the workers if we abort before all workers are * finished hooking themselves up to the dynamic shared memory segment. * * If we die after all the workers have finished hooking themselves up to * the dynamic shared memory segment, we'll mark the two queues to which * we're directly connected as detached, and the worker(s) connected to * those queues will exit, marking any other queues to which they are * connected as detached. This will cause any as-yet-unaware workers * connected to those queues to exit in their turn, and so on, until * everybody exits. * * But suppose the workers which are supposed to connect to the queues to * which we're directly attached exit due to some error before they * actually attach the queues. The remaining workers will have no way of * knowing this. From their perspective, they're still waiting for those * workers to start, when in fact they've already died. */ on_dsm_detach(seg, cleanup_background_workers, PointerGetDatum(wstate)); /* Configure a worker. */ worker.bgw_flags = BGWORKER_SHMEM_ACCESS; worker.bgw_start_time = BgWorkerStart_ConsistentState; worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_main = NULL; /* new worker might not have library loaded */ sprintf(worker.bgw_library_name, "test_shm_mq"); sprintf(worker.bgw_function_name, "test_shm_mq_main"); snprintf(worker.bgw_name, BGW_MAXLEN, "test_shm_mq"); worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(seg)); /* set bgw_notify_pid, so we can detect if the worker stops */ worker.bgw_notify_pid = MyProcPid; /* Register the workers. */ for (i = 0; i < nworkers; ++i) { if (!RegisterDynamicBackgroundWorker(&worker, &wstate->handle[i])) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("could not register background process"), errhint("You may need to increase max_worker_processes."))); ++wstate->nworkers; } /* All done. */ MemoryContextSwitchTo(oldcontext); return wstate; }
Datum worker_test(PG_FUNCTION_ARGS) { int i, nworkers; dsm_segment *seg; test_shm_mq_header *hdr; MemoryContext oldcontext; worker_state *wstate; nworkers = PG_GETARG_INT32(0); #if PG_VERSION_NUM >= 90500 seg = dsm_create(sizeof(test_shm_mq_header), 0); #else seg = dsm_create(sizeof(test_shm_mq_header) #endif hdr = dsm_segment_address(seg); printf("begin worker_test: %d, %p\n", dsm_segment_handle(seg), hdr); MemSet(hdr, 0, sizeof(test_shm_mq_header)); SpinLockInit(&hdr->mutex); strncpy(hdr->dbname, get_database_name(MyDatabaseId), sizeof(hdr->dbname)); oldcontext = MemoryContextSwitchTo(CurTransactionContext); wstate = MemoryContextAlloc(TopTransactionContext, offsetof(worker_state, handle) + sizeof(BackgroundWorkerHandle *) * nworkers); MemSet(wstate, 0, offsetof(worker_state, handle) + sizeof(BackgroundWorkerHandle *) * nworkers); on_dsm_detach(seg, cleanup_background_workers, PointerGetDatum(wstate)); for (i = 0; i < nworkers; i++) { BackgroundWorker worker; MemSet(&worker, 0, sizeof(worker)); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_ConsistentState; worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_main = NULL; /* new worker might not have library loaded */ sprintf(worker.bgw_library_name, "worker_test"); sprintf(worker.bgw_function_name, "worker_test_main"); snprintf(worker.bgw_name, BGW_MAXLEN, "worker_test %d", i); worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(seg)); worker.bgw_notify_pid = MyProcPid; if (!RegisterDynamicBackgroundWorker(&worker, &wstate->handle[i])) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("could not register background process"), errhint("You may need to increase max_worker_processes."))); ++wstate->nworkers; } for (i = 0; i < nworkers; i++) { BgwHandleStatus status; pid_t pid; status = WaitForBackgroundWorkerStartup(wstate->handle[i], &pid); if (status == BGWH_STOPPED) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("could not start background process"), errhint("More details may be available in the server log."))); if (status == BGWH_POSTMASTER_DIED) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("cannot start background processes without postmaster"), errhint("Kill all remaining database processes and restart the database."))); Assert(status == BGWH_STARTED); } wait_for_workers_to_become_ready(wstate, hdr); cancel_on_dsm_detach(seg, cleanup_background_workers, PointerGetDatum(wstate)); dsm_detach(seg); pfree(wstate); MemoryContextSwitchTo(oldcontext); PG_RETURN_VOID(); }
/* * Starts background worker that will create new partitions, * waits till it finishes the job and returns the result (new partition oid) */ Oid create_partitions_bg_worker(Oid relid, Datum value, Oid value_type, bool *crashed) { BackgroundWorker worker; BackgroundWorkerHandle *worker_handle; BgwHandleStatus status; dsm_segment *segment; dsm_handle segment_handle; pid_t pid; PartitionArgs *args; Oid child_oid; TypeCacheEntry *tce; /* Create a dsm segment for the worker to pass arguments */ segment = dsm_create(sizeof(PartitionArgs), 0); segment_handle = dsm_segment_handle(segment); tce = lookup_type_cache(value_type, 0); /* Fill arguments structure */ args = (PartitionArgs *) dsm_segment_address(segment); args->dbid = MyDatabaseId; args->relid = relid; if (tce->typbyval) args->value = value; else memcpy(&args->value, DatumGetPointer(value), sizeof(args->value)); args->by_val = tce->typbyval; args->value_type = value_type; args->result = 0; /* Initialize worker struct */ worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_main = bg_worker_main; worker.bgw_main_arg = Int32GetDatum(segment_handle); worker.bgw_notify_pid = MyProcPid; /* Start dynamic worker */ if (!RegisterDynamicBackgroundWorker(&worker, &worker_handle)) { elog(WARNING, "Unable to create background worker for pg_pathman"); } status = WaitForBackgroundWorkerStartup(worker_handle, &pid); if (status == BGWH_POSTMASTER_DIED) { ereport(WARNING, (errmsg("Postmaster died during the pg_pathman background worker process"), errhint("More details may be available in the server log."))); } /* Wait till the worker finishes its job */ status = WaitForBackgroundWorkerShutdown(worker_handle); *crashed = args->crashed; child_oid = args->result; /* Free dsm segment */ dsm_detach(segment); return child_oid; }