/* **************************************************************************** * * SubscriptionCache::semTake - */ void SubscriptionCache::semTake(void) { if (pthread_self() == mutexOwner) { return; } struct timespec startTime; struct timespec endTime; struct timespec diffTime; if (semTimeStatistics) { clock_gettime(CLOCK_REALTIME, &startTime); } sem_wait(&mutex); mutexOwner = pthread_self(); if (semTimeStatistics) { clock_gettime(CLOCK_REALTIME, &endTime); clock_difftime(&endTime, &startTime, &diffTime); clock_addtime(&subCacheMutexTime, &diffTime); } }
/* **************************************************************************** * * timeStatSemTake - */ int timeStatSemTake(const char* who, const char* what) { int r; LM_T(LmtTimeStatSem, ("%s taking the 'timeStat' semaphore for '%s'", who, what)); struct timespec startTime; struct timespec endTime; struct timespec diffTime; if (semWaitStatistics) { clock_gettime(CLOCK_REALTIME, &startTime); } r = sem_wait(&timeStatSem); if (semWaitStatistics) { clock_gettime(CLOCK_REALTIME, &endTime); clock_difftime(&endTime, &startTime, &diffTime); clock_addtime(&accTimeStatSemTime, &diffTime); } LM_T(LmtTimeStatSem, ("%s has the 'timeStat' semaphore", who)); return r; }
/* A non-monotonic clock safe difference, if the time moved backwards consider the difference as infinite. */ double clock_difftime_safe(struct timespec *after, struct timespec *before) { double diff = clock_difftime(after, before); if (diff >= 0.0) return diff; return DBL_MAX; }
/* **************************************************************************** * * reqSemTake - */ int reqSemTake(const char* who, const char* what, SemOpType reqType, bool* taken) { int r; if (reqPolicy == SemNoneOp) { *taken = false; return -1; } if ((reqPolicy == SemWriteOp) && (reqType == SemReadOp)) { *taken = false; return -1; } if ((reqPolicy == SemReadOp) && (reqType == SemWriteOp)) { *taken = false; return -1; } LM_T(LmtReqSem, ("%s taking the 'req' semaphore for '%s'", who, what)); struct timespec startTime; struct timespec endTime; struct timespec diffTime; if (semWaitStatistics) { clock_gettime(CLOCK_REALTIME, &startTime); } r = sem_wait(&reqSem); if (semWaitStatistics) { clock_gettime(CLOCK_REALTIME, &endTime); clock_difftime(&endTime, &startTime, &diffTime); clock_addtime(&accReqSemTime, &diffTime); } LM_T(LmtReqSem, ("%s has the 'req' semaphore", who)); *taken = true; return r; }
/* **************************************************************************** * * mongoPoolConnectionGet - * * There are two semaphores to get a connection. * - One binary semaphore that protects the connection-vector itself (connectionPoolSem) * - One counting semaphore that makes the caller wait until there is at least one free connection (connectionSem) * * There is a limited number of connections and the first thing to do is to wait for a connection * to become avilable (any of the N connections in the pool) - this is done waiting on the counting semaphore that is * initialized with "POOL SIZE" - meaning the semaphore can be taken N times if the pool size is N. * * Once a connection is free, 'sem_wait(&connectionSem)' returns and we now have to take the semaphore * that protects for pool itself (we *are* going to modify the vector of the pool - can only do it in * one thread at a time ...) * * After taking a connection, the semaphore 'connectionPoolSem' is freed, as all modifications to the connection pool * have finished. * The other semaphore however, 'connectionSem', is kept and it is not freed until we finish using the connection. * * The function mongoPoolConnectionRelease releases the counting semaphore 'connectionSem'. * Very important to call the function 'mongoPoolConnectionRelease' after finishing using the connection ! * */ DBClientBase* mongoPoolConnectionGet(void) { DBClientBase* connection = NULL; struct timespec startTime; struct timespec endTime; struct timespec diffTime; if (semStatistics) { clock_gettime(CLOCK_REALTIME, &startTime); } sem_wait(&connectionSem); sem_wait(&connectionPoolSem); if (semStatistics) { clock_gettime(CLOCK_REALTIME, &endTime); clock_difftime(&endTime, &startTime, &diffTime); clock_addtime(&semWaitingTime, &diffTime); } for (int ix = 0; ix < connectionPoolSize; ++ix) { if (connectionPool[ix].free == true) { connectionPool[ix].free = false; connection = connectionPool[ix].connection; break; } } sem_post(&connectionPoolSem); return connection; }
/* **************************************************************************** * * jsonRequestTreat - */ std::string jsonRequestTreat ( ConnectionInfo* ciP, ParseData* parseDataP, RequestType requestType, JsonDelayedRelease* releaseP, std::vector<std::string>& compV ) { std::string answer; struct timespec start; struct timespec end; if (timingStatistics) { clock_gettime(CLOCK_REALTIME, &start); } switch (requestType) { case EntitiesRequest: // POST /v2/entities releaseP->entity = &parseDataP->ent.res; answer = parseEntity(ciP, &parseDataP->ent.res, false); if (answer != "OK") { return answer; } if ((answer = parseDataP->ent.res.check(ciP, EntitiesRequest)) != "OK") { OrionError error(SccBadRequest, answer); return error.render(ciP, ""); } break; case EntityRequest: // POST|PUT /v2/entities/<eid> releaseP->entity = &parseDataP->ent.res; answer = parseEntity(ciP, &parseDataP->ent.res, true); if (answer != "OK") { return answer; } if ((answer = parseDataP->ent.res.check(ciP, EntityRequest)) != "OK") { OrionError error(SccBadRequest, answer); return error.render(ciP, ""); } break; case EntityAttributeRequest: releaseP->attribute = &parseDataP->attr.attribute; releaseP->attribute->name = compV[4]; answer = parseContextAttribute(ciP, &parseDataP->attr.attribute); if (answer != "OK") { return answer; } if ((answer = parseDataP->attr.attribute.check(ciP, EntityAttributeRequest, "", "", 0)) != "OK") { OrionError error(SccBadRequest, answer); return error.render(ciP, ""); } break; case EntityAttributeValueRequest: releaseP->attribute = &parseDataP->av.attribute; answer = parseAttributeValue(ciP, &parseDataP->av.attribute); if (answer != "OK") { return answer; } break; case SubscriptionsRequest: answer = parseSubscription(ciP, &parseDataP->subsV2); if (answer != "OK") { return answer; } break; case IndividualSubscriptionRequest: answer = parseSubscription(ciP, &parseDataP->subsV2, true); // NOTE: partial == true if (answer != "OK") { return answer; } break; case BatchQueryRequest: answer = parseBatchQuery(ciP, &parseDataP->bq.res); if (answer != "OK") { return answer; } break; case BatchUpdateRequest: answer = parseBatchUpdate(ciP, &parseDataP->bu.res); if (answer != "OK") { return answer; } break; default: OrionError error(SccNotImplemented, "Request Treat function not implemented"); answer = error.render(ciP, ""); ciP->httpStatusCode = SccNotImplemented; break; } if (timingStatistics) { clock_gettime(CLOCK_REALTIME, &end); clock_difftime(&end, &start, &threadLastTimeStat.jsonV2ParseTime); } return answer; }
/* **************************************************************************** * * requestCompleted - */ static void requestCompleted ( void* cls, MHD_Connection* connection, void** con_cls, MHD_RequestTerminationCode toe ) { ConnectionInfo* ciP = (ConnectionInfo*) *con_cls; struct timespec reqEndTime; if ((ciP->payload != NULL) && (ciP->payload != static_buffer)) { free(ciP->payload); } *con_cls = NULL; lmTransactionEnd(); // Incoming REST request ends if (timingStatistics) { clock_gettime(CLOCK_REALTIME, &reqEndTime); clock_difftime(&reqEndTime, &ciP->reqStartTime, &threadLastTimeStat.reqTime); } delete(ciP); // // Statistics // // Flush this requests timing measures onto a global var to be read by "GET /statistics". // Also, increment the accumulated measures. // if (timingStatistics) { timeStatSemTake(__FUNCTION__, "updating statistics"); memcpy(&lastTimeStat, &threadLastTimeStat, sizeof(lastTimeStat)); // // "Fix" mongoBackendTime // Substract times waiting at mongo driver operation (in mongo[Read|Write|Command]WaitTime counters) so mongoBackendTime // contains at the end the time passed in our logic, i.e. a kind of "self-time" for mongoBackend // clock_subtime(&threadLastTimeStat.mongoBackendTime, &threadLastTimeStat.mongoReadWaitTime); clock_subtime(&threadLastTimeStat.mongoBackendTime, &threadLastTimeStat.mongoWriteWaitTime); clock_subtime(&threadLastTimeStat.mongoBackendTime, &threadLastTimeStat.mongoCommandWaitTime); clock_addtime(&accTimeStat.jsonV1ParseTime, &threadLastTimeStat.jsonV1ParseTime); clock_addtime(&accTimeStat.jsonV2ParseTime, &threadLastTimeStat.jsonV2ParseTime); clock_addtime(&accTimeStat.mongoBackendTime, &threadLastTimeStat.mongoBackendTime); clock_addtime(&accTimeStat.mongoWriteWaitTime, &threadLastTimeStat.mongoWriteWaitTime); clock_addtime(&accTimeStat.mongoReadWaitTime, &threadLastTimeStat.mongoReadWaitTime); clock_addtime(&accTimeStat.mongoCommandWaitTime, &threadLastTimeStat.mongoCommandWaitTime); clock_addtime(&accTimeStat.renderTime, &threadLastTimeStat.renderTime); clock_addtime(&accTimeStat.reqTime, &threadLastTimeStat.reqTime); timeStatSemGive(__FUNCTION__, "updating statistics"); } }
/* **************************************************************************** * * get_curl_context_reuse - */ static int get_curl_context_reuse(const std::string& key, struct curl_context* pcc) { pcc->curl = NULL; pcc->pmutex = NULL; int s = pthread_mutex_lock(&contexts_mutex); if (s != 0) { LM_E(("Runtime Error (pthread_mutex_lock failure)")); return s; } std::map<std::string, struct curl_context>::iterator it; it = contexts.find(key); if (it == contexts.end()) { // not found, create it pcc->curl = curl_easy_init(); if (pcc->curl != NULL) { pthread_mutex_t* pm = (pthread_mutex_t *) malloc(sizeof(*pm)); if (pm == NULL) { pthread_mutex_unlock(&contexts_mutex); LM_E(("Runtime Error (malloc)")); return -1; } int s = pthread_mutex_init(pm, NULL); if (s != 0) { pthread_mutex_unlock(&contexts_mutex); LM_E(("Runtime Error (pthread_mutex_init)")); free(pm); return s; } pcc->pmutex = pm; contexts[key] = *pcc; } else // curl_easy returned null { pcc->pmutex = NULL; // unnecessary but clearer } } else // previous context found { *pcc = it->second; } s = pthread_mutex_unlock(&contexts_mutex); if (s != 0) { LM_E(("Runtime Error (pthread_mutex_unlock)")); return s; } // lock the mutex, if everything was right // and cc is not {NULL, NULl} if (pcc->pmutex != NULL) { struct timespec startTime; struct timespec endTime; struct timespec diffTime; if (semWaitStatistics) { clock_gettime(CLOCK_REALTIME, &startTime); } s = pthread_mutex_lock(pcc->pmutex); if (s != 0) { LM_E(("Runtime Error (pthread_mutex_lock)")); return s; } if (semWaitStatistics) { clock_gettime(CLOCK_REALTIME, &endTime); clock_difftime(&endTime, &startTime, &diffTime); int s = pthread_mutex_lock(&contexts_mutex); if (s != 0) { LM_E(("Runtime Error (pthread_mutex_lock)")); return s; } clock_addtime(&accCCMutexTime, &diffTime); s = pthread_mutex_unlock(&contexts_mutex); if (s != 0) { LM_E(("Runtime Error (pthread_mutex_unlock)")); return s; } } } return 0; }
/* **************************************************************************** * * xmlTreat - */ std::string xmlTreat ( const char* content, ConnectionInfo* ciP, ParseData* parseDataP, RequestType request, std::string payloadWord, XmlRequest** reqPP, std::string* errorMsgP ) { xml_document<> doc; char* xmlPayload = (char*) content; struct timespec start; struct timespec end; // // If the payload is empty, the XML parsing library does an assert // and the broker dies. // Therefore, this check here is important, to avoid death. // // 'OK' is returned as there is no error to send a request without payload. // if ((content == NULL) || (*content == 0)) { return "OK"; } try { if (timingStatistics) { clock_gettime(CLOCK_REALTIME, &start); } doc.parse<0>(xmlPayload); } catch (parse_error& e) { std::string errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", "unknown", SccBadRequest, "XML Parse Error"); LM_W(("Bad Input ('%s', '%s')", content, e.what())); if (errorMsgP) { *errorMsgP = std::string("XML parse error exception: ") + e.what(); } return errorReply; } // in this case the try/catch block is not using a 'catch (const std::exception &e)' clause, as we are not using // e.what(), so it wouldn't be useful catch (...) { std::string errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", "unknown", SccBadRequest, "XML Parse Error"); LM_W(("Bad Input (%s)", content)); if (errorMsgP) { *errorMsgP = std::string("XML parse generic exception"); } return errorReply; } xml_node<>* father = doc.first_node(); XmlRequest* reqP = xmlRequestGet(request, ciP->method); ciP->parseDataP = parseDataP; if (father == NULL) { std::string errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", "unknown", SccBadRequest, "XML Parse Error"); LM_W(("Bad Input (XML parse error)")); if (errorMsgP) { *errorMsgP = std::string("XML parse error: invalid XML input"); } return errorReply; } if (reqP == NULL) { std::string errorReply = restErrorReplyGet( ciP, ciP->outFormat, "", requestType(request), SccBadRequest, std::string("Sorry, no request treating object found for RequestType /") + requestType(request) + "/, method /" + ciP->method + "/"); LM_W(("Bad Input (no request treating object found for RequestType %d (%s), method %s)", request, requestType(request), ciP->method.c_str())); LM_W(("Bad Input (no request treating object found for RequestType %d (%s), method %s)", request, requestType(request), ciP->method.c_str())); if (errorMsgP) { *errorMsgP = std::string("Unable to treat ") + requestType(request) + " requests"; } return errorReply; } if (reqPP != NULL) { *reqPP = reqP; } // // Checking that the payload matches the URL // if (((ciP->verb == POST) || (ciP->verb == PUT)) && (payloadWord.length() != 0)) { std::string errorReply; char* payloadStart = (char*) content; // Skip '<?xml version="1.0" encoding="UTF-8"?> ' ? if (strncmp(payloadStart, "<?xml", 5) == 0) { ++payloadStart; payloadStart = strstr(payloadStart, "<"); } // Skip '<' if (*payloadStart == '<') { ++payloadStart; } if (strncasecmp(payloadWord.c_str(), payloadStart, payloadWord.length()) != 0) { errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", reqP->keyword, SccBadRequest, std::string("Expected /") + payloadWord + "/ payload, got /" + payloadStart + "/"); LM_W(("Bad Input (invalid payload: wanted: '%s', got '%s')", payloadWord.c_str(), payloadStart)); if (errorMsgP) { *errorMsgP = std::string("Bad Input (invalid payload, expecting '") + payloadWord + "', got '" + payloadStart + "')"; } return errorReply; } } if (reqP->init == NULL) // No payload treating function { return "OK"; } reqP->init(parseDataP); ciP->httpStatusCode = SccOk; xmlParse(ciP, NULL, father, "", "", reqP->parseVector, parseDataP, errorMsgP); if (timingStatistics) { clock_gettime(CLOCK_REALTIME, &end); clock_difftime(&end, &start, &threadLastTimeStat.xmlParseTime); } if (ciP->httpStatusCode != SccOk) { LM_W(("Bad Input (XML parse error)")); return restErrorReplyGet(ciP, ciP->outFormat, "", payloadWord, ciP->httpStatusCode, ciP->answer); } LM_T(LmtParseCheck, ("Calling check for XML parsed tree (%s)", ciP->payloadWord)); std::string check = reqP->check(parseDataP, ciP); if (check != "OK") { LM_W(("Bad Input (%s: %s)", reqP->keyword.c_str(), check.c_str())); if (errorMsgP) { *errorMsgP = std::string("Bad Input: ") + check; } } if (check != "OK") { if (errorMsgP) { *errorMsgP = std::string("Bad Input: ") + check; } } return check; }
/* **************************************************************************** * * QueueWorkers::start() - */ static void *workerFunc(void* pSyncQ) { SyncQOverflow<SenderThreadParams*> *queue = (SyncQOverflow<SenderThreadParams*> *) pSyncQ; CURL *curl; // Initialize curl context curl = curl_easy_init(); if (curl == NULL) { LM_E(("Runtime Error (curl_easy_init)")); pthread_exit(NULL); } for (;;) { SenderThreadParams* params = queue->pop(); struct timespec now; struct timespec howlong; size_t estimatedQSize; QueueStatistics::incOut(); clock_gettime(CLOCK_REALTIME, &now); clock_difftime(&now, ¶ms->timeStamp, &howlong); estimatedQSize = queue->size(); QueueStatistics::addTimeInQWithSize(&howlong, estimatedQSize); strncpy(transactionId, params->transactionId, sizeof(transactionId)); LM_T(LmtNotifier, ("worker sending to: host='%s', port=%d, verb=%s, tenant='%s', service-path: '%s', xauthToken: '%s', path='%s', content-type: %s", params->ip.c_str(), params->port, params->verb.c_str(), params->tenant.c_str(), params->servicePath.c_str(), params->xauthToken.c_str(), params->resource.c_str(), params->content_type.c_str())); if (simulatedNotification) { LM_T(LmtNotifier, ("simulatedNotification is 'true', skipping outgoing request")); __sync_fetch_and_add(&noOfSimulatedNotifications, 1); } else // we'll send the notification { std::string out; int r; r = httpRequestSendWithCurl(curl, params->ip, params->port, params->protocol, params->verb, params->tenant, params->servicePath, params->xauthToken, params->resource, params->content_type, params->content, true, NOTIFICATION_WAIT_MODE, &out); // // FIXME: ok and error counter should be incremented in the other notification modes (generalizing the concept, i.e. // not as member of QueueStatistics:: which seems to be tied to just the threadpool notification mode) // if (r == 0) { statisticsUpdate(NotifyContextSent, params->format); QueueStatistics::incSentOK(); } else { QueueStatistics::incSentError(); } } // Free params memory delete params; // Reset curl for next iteration curl_easy_reset(curl); } }