/** ======================================================================== * ========================================================================= */ PslError psl_inet_connect_sock(int const s, int const addrFamily, const char* const addrStr, int const port, const char* const userLabel, const void* const cookie) { PslInetGenericSockAddr server; PSL_LOG_DEBUG("%s (%s=%p): fd=%d, addrfamily=%d, addrStr=%s, port=%d", __func__, userLabel, cookie, s, addrFamily, PSL_LOG_OBFUSCATE_STR(addrStr), port); bool success = !psl_inet_make_sock_addr(addrFamily, addrStr, port, &server, userLabel, cookie); if (!success) { return PSL_ERR_BAD_SERV_ADDR; } int rc = connect(s, (struct sockaddr*)&server.sa, server.addrLength); PSL_ASSERT(rc <= 0); if (0 == rc) { PSL_LOG_DEBUG("%s (%s=%p): connect() succeeded immediately", __func__, userLabel, cookie); return 0; } else if (rc < 0 && EINPROGRESS == errno) { PSL_LOG_DEBUG("%s (%s=%p): connect() returned EINPROGRESS", __func__, userLabel, cookie); return 0; } /* @todo The implication of EINTR when connecting on a non-blocking socket is vague, at best. Should we retry connect(), assume that all is well, or give up on this connection? else if (rc < 0 && EINTR == errno) { PSL_LOG_DEBUG("%s (fsm=%p): connect() returned EINTR", __func__, pFsm); return 0; } */ else { /// (rc < 0) int const saverrno = errno; PSL_LOG_ERROR("%s (%s=%p): ERROR: connect() failed; errno=%d (%s)", __func__, userLabel, cookie, saverrno, strerror(saverrno)); return psl_err_pslerror_from_connect_errno(saverrno); } }//psl_inet_connect_sock
/* ========================================================================= * ========================================================================= */ PslError PmSockThreadCtxNewFromGMain(GMainContext* gmainCtx, const char* const userLabel, PmSockThreadContext** const pThreadCtx) { PSL_LOG_DEBUG("%s: pThreadCtx=%p, userLabel=\"%s\", gmaincxt=%p", __func__, pThreadCtx, PSL_LOG_MAKE_SAFE_STR(userLabel), gmainCtx); PSL_ASSERT(pThreadCtx); *pThreadCtx = NULL; struct PmSockThreadContext_* const ctx = g_new0(struct PmSockThreadContext_, 1); PslError pslerr = 0; if (!ctx) { pslerr = PSL_ERR_MEM; goto error_cleanup; } psl_refcount_init(&ctx->refCount_, "PSL_THREAD_CTX", ctx); ctx->userLabel = g_strdup(userLabel ? userLabel : "PSL_user"); if (!ctx->userLabel) { pslerr = PSL_ERR_MEM; goto error_cleanup; } gmainCtx = gmainCtx ? gmainCtx : g_main_context_default(); ctx->gmainCtx = g_main_context_ref(gmainCtx); PSL_LOG_DEBUG("%s (%s): PSL Thread Context created: ctx=%p", __func__, ctx->userLabel, ctx); *pThreadCtx = ctx; return 0; error_cleanup: PSL_ASSERT(pslerr); PSL_LOG_FATAL("%s (%s): ERROR: PslError=%d (%s)", __func__, PSL_LOG_MAKE_SAFE_STR(userLabel), (int)pslerr, PmSockErrStringFromError(pslerr)); if (ctx) { thread_context_destroy_internal(ctx); } return pslerr; }//PmSockThreadCtxNewFromGMain
/** ======================================================================== * ========================================================================= */ PslError psl_multi_fd_watch_reset(PslMultiFdWatchSource* source) { PSL_LOG_DEBUG("%s (watch=%p): ENTERING", __func__, source); source->restartTimer = true; source->timeoutMillisec = MULTI_FD_WATCH_INFINITE_MSEC; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, source->descTable); while (g_hash_table_iter_next(&iter, &key, &value)) { PslMultiFdDescriptor* desc = (PslMultiFdDescriptor*)value; const gint fd = GPOINTER_TO_INT(key); PSL_ASSERT(fd == desc->pollFd.fd); if (desc->pollAdded) { g_source_remove_poll((GSource*)source, &desc->pollFd); } g_hash_table_iter_remove(&iter); } PSL_LOG_DEBUGLOW("%s (watch=%p): LEAVING", __func__, source); return 0; }
/** ======================================================================== * ========================================================================= */ PslError psl_multi_fd_watch_set_poll_timeout(PslMultiFdWatchSource* source, int const timeoutMillisec) { PSL_LOG_DEBUG("%s (watch=%p), timeout=%d millisec", __func__, source, (int)timeoutMillisec); if (timeoutMillisec < -1) { PSL_LOG_ERROR("%s (watch=%p): ERROR: passed negative timemeout, " \ "but not -1: %d", __func__, source, (int)timeoutMillisec); return PSL_ERR_INVAL; } source->restartTimer = true; if (timeoutMillisec == -1) { source->timeoutMillisec = MULTI_FD_WATCH_INFINITE_MSEC; } else { // timeoutMillisec >= 0 source->timeoutMillisec = timeoutMillisec; } return 0; }
/** ======================================================================== * ========================================================================= */ PslMultiFdWatchSource* psl_multi_fd_watch_new(void) { GSource* const base = g_source_new(&psl_multi_fd_watch_funcs, sizeof(PslMultiFdWatchSource)); PslMultiFdWatchSource* const source = (PslMultiFdWatchSource*)base; if (!base) { goto error_cleanup; } /** * @note g_source_new returns a zero-initialized structure with * the base (GSource) "class" properly set up and with * reference count = 1. */ source->restartTimer = true; source->timeoutMillisec = MULTI_FD_WATCH_INFINITE_MSEC; source->descTable = g_hash_table_new_full(&g_direct_hash, &g_direct_equal, NULL /*key_destroy_func*/, &g_free/*value_destroy_func*/); if (!source->descTable) { goto error_cleanup; } PSL_LOG_DEBUGLOW("%s (watch=%p): descriptor table=%p", __func__, source, source->descTable); source->cachedReadyPollFdArray = g_array_new(false/*zero_terminated*/, false/*clear_*/, sizeof(PslMultiFdPollRec)); if (!source->cachedReadyPollFdArray) { goto error_cleanup; } PSL_LOG_DEBUG("%s (watch=%p): new watch created", __func__, source); return source; error_cleanup: PSL_LOG_FATAL("%s: FAILED (out of mem?)", __func__); if (base) { g_source_unref(base); } return NULL; }
/** ======================================================================== * ========================================================================= */ void psl_chan_fsm_finalize(PslChanFsm* const pFsm) { PSL_LOG_DEBUG("%s (fsm=%p)", __func__, pFsm); PSL_ASSERT(pFsm); if (pFsm->fsmStarted) { if (!pFsm->fsmClosed) { psl_chan_fsm_evt_dispatch_CLOSE(pFsm); PSL_ASSERT(pFsm->fsmClosed); } /// Trigger exits out of the active states to ensure orderly clean-up psl_chan_fsm_evt_dispatch_FINALIZE(pFsm); } }///psl_chan_fsm_finalize
/** ======================================================================== * ========================================================================= */ PslError psl_multi_fd_watch_add_or_update_fd(PslMultiFdWatchSource* const source, int const fd, GIOCondition const condition) { PslMultiFdDescriptor* desc; PSL_LOG_DEBUG("%s (watch=%p): fd=%d, GIOCondition=0x%lX", __func__, source, (int)fd, (unsigned long)condition); desc = (PslMultiFdDescriptor*) g_hash_table_lookup(source->descTable, GINT_TO_POINTER(fd)); /** * @note multi_fd_watch_prepare() takes care of adding/removing * our pollFd's from the source's poll list as needed */ if (desc) { PSL_LOG_DEBUGLOW( "%s (watch=%p): found desc=%p with fd=%d in descriptor table=%p", __func__, source, desc, (int)fd, source->descTable); desc->pollFd.events = condition; } else { /// create a new descriptor and add it to the poll set desc = g_new0(PslMultiFdDescriptor, 1); if (!desc) { PSL_LOG_FATAL("%s (watch=%p): ERROR: g_new0 failed", __func__, source); return PSL_ERR_MEM; } desc->pollFd.fd = fd; desc->pollFd.events = condition; g_hash_table_insert(source->descTable, GINT_TO_POINTER(desc->pollFd.fd), desc); PSL_LOG_DEBUGLOW( "%s (watch=%p): allocated desc=%p for fd=%d in descriptor table=%p", __func__, source, desc, (int)fd, source->descTable); } return 0; }
/* ========================================================================= * ========================================================================= */ static void thread_context_destroy_internal(PmSockThreadContext* const ctx) { PSL_LOG_DEBUG("%s (ctx=%p/%s)", __func__, ctx, PSL_LOG_MAKE_SAFE_STR(ctx->userLabel)); PSL_ASSERT(ctx); g_free(ctx->userLabel); if (ctx->gmainCtx) { g_main_context_unref(ctx->gmainCtx); } g_free(ctx); PSL_LOG_DEBUGLOW("%s (ctx=%p): LEAVING", __func__, ctx); }
/** ======================================================================== * ========================================================================= */ static PslSmeEventStatus chan_fsm_init_state_handler(PslChanFsmInitState* const pState, PslChanFsm* const pFsm, PslSmeEventId const evtId, const PslChanFsmEvtArg* const evtArg) { switch (evtId) { case kFsmEventEnterScope: case kFsmEventExitScope: case kFsmEventBegin: return kPslSmeEventStatus_success; break; case PSL_CHAN_FSM_EVT_CONNECT: if (AF_UNSPEC == pFsm->userSettings.serverAddr.addrFamily && pFsm->fd < 0) { PSL_LOG_ERROR("%s (fsm=%p): ERROR: PSL_CHAN_FSM_EVT_CONNECT " \ "failed: connect address not set", __func__, pFsm); psl_chan_fsm_set_last_error(pFsm, kPslChanFsmErrorSource_psl, PSL_ERR_BAD_SERV_ADDR); return kPslSmeEventStatus_error; } if (pFsm->fd < 0) { psl_chan_fsm_goto_plain_lookup_state(pFsm, &evtArg->connect.data); } else { static const struct PslChanFsmEvtInetAddrText addrText = { .family = AF_UNSPEC }; psl_chan_fsm_goto_plain_conn_state(pFsm, &evtArg->connect.data, &addrText); } return kPslSmeEventStatus_success; break; case PSL_CHAN_FSM_EVT_SET_CONN_FD: { const struct PslChanFsmEvtArgSetConnFD* const arg = &evtArg->setConnFD; PSL_ASSERT(arg->fd >= 0); if (AF_UNSPEC != pFsm->userSettings.serverAddr.addrFamily) { PSL_LOG_ERROR("%s (fsm=%p): SET_CONN_FD ERROR: " \ "not allowed when server address is set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } if (AF_UNSPEC != pFsm->userSettings.bindAddr.addrFamily) { PSL_LOG_ERROR("%s (fsm=%p): SET_CONN_FD ERROR: " \ "not allowed when bind address is set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } if (pFsm->fd >= 0) { PSL_LOG_ERROR("%s (fsm=%p): SET_CONN_FD ERROR: connected " \ "file descriptor was already set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } pFsm->fd = arg->fd; pFsm->userSettings.fdIsUserProvided = true; pFsm->userSettings.fdOpts = arg->opts; return kPslSmeEventStatus_success; break; } case PSL_CHAN_FSM_EVT_SET_SERVER: { const struct PslChanFsmEvtArgSetServer* const arg = &evtArg->setServer; if (pFsm->fd >= 0) { PSL_LOG_ERROR("%s (fsm=%p): SET_SERVER ADDR ERROR: not " \ "allowed when connected file descriptor is set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } if (AF_UNSPEC != pFsm->userSettings.serverAddr.addrFamily) { PSL_LOG_ERROR("%s (fsm=%p): SET_SERVER ADDR ERROR: " \ "server address was already set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } PSL_LOG_DEBUG("%s (fsm=%p): SET_SERVER ADDR: server: '%s:%d'/af=%d", __func__, pFsm, PSL_LOG_OBFUSCATE_STR(arg->hostStr), (int)arg->port, (int)arg->addrFamily); pFsm->userSettings.serverAddr.addrFamily = arg->addrFamily; pFsm->userSettings.serverAddr.port = arg->port; pFsm->userSettings.serverAddr.hostStr = g_strdup(arg->hostStr); PSL_ASSERT(pFsm->userSettings.serverAddr.hostStr); } return kPslSmeEventStatus_success; break; case PSL_CHAN_FSM_EVT_SET_SOCK_BIND: { const struct PslChanFsmEvtArgSetSockBind* const arg = &evtArg->setSockBind; if (pFsm->fd >= 0) { PSL_LOG_ERROR("%s (fsm=%p): SET_SOCK_BIND ERROR: not " \ "allowed when connected file descriptor is set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } if (AF_UNSPEC != pFsm->userSettings.bindAddr.addrFamily) { PSL_LOG_ERROR("%s (fsm=%p): SET_SOCK_BIND ERROR: " \ "socket bind address was already set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } PSL_LOG_DEBUG("%s (fsm=%p): SET_SOCK_BIND: local addr: '%s:%d'/af=%d", __func__, pFsm, PSL_LOG_MAKE_SAFE_STR(arg->ipAddrStr), (int)arg->port, (int)arg->addrFamily); pFsm->userSettings.bindAddr.addrFamily = arg->addrFamily; pFsm->userSettings.bindAddr.port = arg->port; pFsm->userSettings.bindAddr.hostStr = g_strdup(arg->ipAddrStr); PSL_ASSERT(!arg->ipAddrStr || pFsm->userSettings.bindAddr.hostStr); } return kPslSmeEventStatus_success; break; default: break; ///< allow default processing by parent } return kPslSmeEventStatus_passToParent; }//chan_fsm_init_state_handler
/** ======================================================================== * ========================================================================= */ void psl_chan_fsm_dispatch_completion_cb( PslChanFsm* const pFsm, const PslChanFsmEvtCompletionCbInfo* const pCb, PslError const pslErr, bool const switchingToCrypto) { PSL_ASSERT(pFsm); PSL_ASSERT(pCb); if (pFsm->fsmClosed) { PSL_LOG_NOTICE("%s (fsm=%p): FSM is closed: suppressing callbacks", __func__, pFsm); return; } enum PslChanFsmEvtCompletionCbId which = pCb->which; if (kPslChanFsmEvtCompletionCbId_none == which) { PSL_LOG_DEBUG("%s (fsm=%p): no callback to dispatch", __func__, pFsm); return; } /// Process external callback /// So we don't get destroyed while in callback (void)psl_chan_fsm_ref(pFsm); switch (which) { case kPslChanFsmEvtCompletionCbId_none: break; case kPslChanFsmEvtCompletionCbId_completion: { which = kPslChanFsmEvtCompletionCbId_none; PSL_LOG_DEBUG( "%s (fsm=%p/ch=%p): Dispatching 'completion' callback " \ "to user: PslError=%d (%s)", __func__, pFsm, pFsm->channel, (int)pslErr, PmSockErrStringFromError(pslErr)); PSL_ASSERT(pCb->u.completionCb.func); pCb->u.completionCb.func((PmSockIOChannel*)pFsm->channel, pFsm->userSettings.userData, pslErr); } break; case kPslChanFsmEvtCompletionCbId_connect: /// legacy support { which = kPslChanFsmEvtCompletionCbId_none; GError* gerr = NULL; if (pslErr) { gerr = g_error_new(PmSockErrGErrorDomain(), pslErr, "%s", PmSockErrStringFromError(pslErr)); } PSL_LOG_DEBUG( "%s (fsm=%p/ch=%p): Dispatching 'connect' callback " \ "to legacy user: PslError=%d (%s)", __func__, pFsm, pFsm->channel, gerr ? (int)gerr->code : 0, gerr ? gerr->message : "success"); PSL_ASSERT(pCb->u.connectCb.func); (void)pCb->u.connectCb.func(pFsm->channel, pCb->u.connectCb.userData, gerr); if (gerr) {g_error_free(gerr);} } break; case kPslChanFsmEvtCompletionCbId_switch: /// legacy support { which = kPslChanFsmEvtCompletionCbId_none; GError* gerr = NULL; if (pslErr) { gerr = g_error_new(PmSockErrGErrorDomain(), pslErr, "%s", PmSockErrStringFromError(pslErr)); } PSL_LOG_DEBUG( "%s (fsm=%p/ch=%p): Dispatching 'security switch' callback " \ "to legacy user: switchingToCrypto=%d, PslError code=%d (%s)", __func__, pFsm, pFsm->channel, switchingToCrypto, gerr ? (int)gerr->code : 0, gerr ? gerr->message : "success"); PSL_ASSERT(pCb->u.switchCb.func); (void)pCb->u.switchCb.func(pFsm->channel, switchingToCrypto, pCb->u.switchCb.userData, gerr); if (gerr) {g_error_free(gerr);} } break; } PSL_ASSERT(kPslChanFsmEvtCompletionCbId_none == which); /// okay if the FSM gets destroyed now psl_chan_fsm_unref(pFsm); return; }//psl_chan_fsm_dispatch_completion_cb
/** ======================================================================== * ========================================================================= */ PslError psl_chan_fsm_new(PslChanFsm** const fsmResult, PmSockThreadContext* const threadCtx, PmSockOptionFlags const options, GIOChannel* const channel, const char* const userLabel) { PSL_ASSERT(fsmResult && threadCtx); PslChanFsm* fsm = g_new0(PslChanFsm, 1); PSL_ASSERT(fsm); /** * @note chan_fsm_free() logic depends on the refcount being * properly initialized */ psl_refcount_init(&fsm->refCount, "PSL_FSM", fsm); fsm->channel = channel; fsm->fd = PSL_CHAN_FSM_CLOSED_FD_VALUE; /// Init userSettings fsm->userSettings.bindAddr.addrFamily = AF_UNSPEC; fsm->userSettings.serverAddr.addrFamily = AF_UNSPEC; fsm->userSettings.threadCtx = PmSockThreadCtxRef(threadCtx); PSL_ASSERT(!(options & ~kPmSockOptFlag_allValidOpts)); fsm->userSettings.chanOpts = options; /// Create a multi-fd watch source instance fsm->fdWatchInfo.fdWatch = psl_multi_fd_watch_new(); PSL_ASSERT(fsm->fdWatchInfo.fdWatch); g_source_set_can_recurse((GSource*)fsm->fdWatchInfo.fdWatch, false); g_source_set_callback((GSource*)fsm->fdWatchInfo.fdWatch, (GSourceFunc)&chan_fsm_fd_watch_cb, fsm, NULL); g_source_set_priority((GSource*)fsm->fdWatchInfo.fdWatch, G_PRIORITY_HIGH); g_source_attach((GSource*)fsm->fdWatchInfo.fdWatch, fsm->userSettings.threadCtx->gmainCtx); /// Set up our FSM psl_sme_init_machine(&fsm->base, "PSL_CHAN", sizeof(fsm->beginEvtArgSupport.requestArgBuf), &fsm->beginEvtArgSupport.requestArgBuf, &fsm->beginEvtArgSupport.dispatchArgBuf); FsmDbgEnableLoggingViaPmLogLib(&fsm->base.base, kFsmDbgLogOptEvents, gPslLogContext, channel/*cookie*/); /// Initialize and insert our common states into the FSM psl_sme_init_state( &fsm->finalState.base, (PslSmeStateHandlerFnType*)&chan_fsm_final_state_handler, "FINAL"); psl_sme_init_state( &fsm->aliveState.base, (PslSmeStateHandlerFnType*)&chan_fsm_alive_state_handler, "ALIVE"); psl_sme_init_state( &fsm->aliveState.initState.base, (PslSmeStateHandlerFnType*)&chan_fsm_init_state_handler, "INIT"); psl_sme_init_state( &fsm->aliveState.closedState.base, (PslSmeStateHandlerFnType*)&chan_fsm_closed_state_handler, "CLOSED"); psl_sme_insert_state( &fsm->base, &fsm->finalState.base, NULL/*parent*/); psl_sme_insert_state( &fsm->base, &fsm->aliveState.base, NULL/*parent*/); psl_sme_insert_state( &fsm->base, &fsm->aliveState.initState.base, &fsm->aliveState.base/*parent*/); psl_sme_insert_state( &fsm->base, &fsm->aliveState.closedState.base, &fsm->aliveState.base/*parent*/); /// Initialize and insert the plaintext mode states psl_chan_fsm_plain_init(fsm, &fsm->aliveState.base); /// Initialize and insert the crypto mode states psl_chan_fsm_crypto_init(fsm, &fsm->aliveState.base); /// Start the FSM at the "init" state psl_sme_fsm_start(&fsm->base, &fsm->aliveState.initState.base); fsm->fsmStarted = true; PSL_LOG_DEBUG("%s (fsm=%p/%s): created", __func__, fsm, PSL_LOG_MAKE_SAFE_STR(userLabel)); *fsmResult = fsm; return 0; }
/** ======================================================================== * The 'dispatch' function of GSourceFuncs * * @param base * @param opaqueCb @see GSourceFuncs * @param userData @see GSourceFuncs * * @return gboolean @see GSourceFuncs * * ========================================================================= */ static gboolean multi_fd_watch_dispatch(GSource* const base, GSourceFunc opaqueCb, gpointer userData) { PslMultiFdWatchSource* const source = (PslMultiFdWatchSource*)base; PslMultiFdWatchSourceCb* const cb = (PslMultiFdWatchSourceCb*)opaqueCb; source->restartTimer = true; ///< so it will restart at next poll prepare if (!cb) { PSL_LOG_ERROR("%s (watch=%p): ERROR: multi-fd watch dispatch with " \ "NULL user callback ptr: did you forget to call " \ "g_source_set_callback()?", __func__, base); return false; } PSL_LOG_DEBUGLOW("%s (watch=%p): preparing to call user's callback", __func__, base); /// Construct an array of ready PollFD's if (source->cachedReadyPollFdArray->len > 0) { g_array_set_size(source->cachedReadyPollFdArray, 0); } gpointer key, value; GHashTableIter iter; g_hash_table_iter_init(&iter, source->descTable); while (g_hash_table_iter_next(&iter, &key, &value)) { const GPollFD* const pollFd = &((PslMultiFdDescriptor*)value)->pollFd; PSL_LOG_DEBUGLOW( "%s (watch=%p): iter: table=%p, desc=%p, fd=%d, " \ "GIOCondition=(mon:0x%lX, ind:0x%lX)", __func__, source, source->descTable, value, (int)pollFd->fd, (unsigned long)pollFd->events, (unsigned long)pollFd->revents); if ((pollFd->revents & MULTI_FD_WATCH_PERM_FAILURE_IO_CONDS) != 0) { PSL_LOG_ERROR("%s: (watch=%p): I/O FAILURE on fd=%d: indicated " \ "GIOCondition=0x%lX", __func__, source, (int)pollFd->fd, (unsigned long)pollFd->revents); } const GIOCondition currentCondition = pollFd->revents & (pollFd->events | MULTI_FD_WATCH_PERM_FAILURE_IO_CONDS); if (currentCondition) { const PslMultiFdPollRec pollRec = { .fd = pollFd->fd, .reqEvents = pollFd->events, .indEvents = currentCondition }; g_array_append_vals(source->cachedReadyPollFdArray, &pollRec, 1); } } /// Dispatch the callback const gint numrecs = source->cachedReadyPollFdArray->len; const PslMultiFdPollRec* const pollrecs = ((numrecs > 0) ? (PslMultiFdPollRec*)source->cachedReadyPollFdArray->data : NULL); PSL_LOG_DEBUGLOW("%s (watch=%p): Calling user's callback: " \ "pollrec array=%p, numelts=%d", __func__, base, pollrecs, (int)numrecs); const gboolean stayAttached = cb(userData, pollrecs, numrecs); if (!stayAttached) { PSL_LOG_DEBUG("%s (watch=%p): user cb requested removal of source", __func__, base); } return stayAttached; } //multi_fd_watch_dispatch /** ======================================================================== * The 'finalize' function of GSourceFuncs * * @param base * * ========================================================================= */ static void multi_fd_watch_finalize(GSource* base) { PSL_LOG_DEBUG("%s (watch=%p)", __func__, base); PslMultiFdWatchSource* const source = (PslMultiFdWatchSource*)base; /** * @note We may be called with our own data members in a * partially-constructed (but initialized) state if * psl_multi_fd_watch_new failed part way through and * called g_source_unref. */ if (source->descTable) { /** * @note our "superclass" GSource takes care of removing * our pollFds record pointers from the source's and the * gmain context's pollFd lists when the GSource instance * is being detached from the gmain context, so we don't * need to do it ourselves. */ g_hash_table_destroy(source->descTable); } if (source->cachedReadyPollFdArray) { (void)g_array_free(source->cachedReadyPollFdArray, true/*free_segment*/); } }