comparison Plugins/Engine/OrthancPlugins.cpp @ 2000:39329372b667

Speedup in plugins by removing unnecessary locks
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 31 May 2016 12:19:53 +0200
parents 364cc624eb65
children 6301bbcbcaed
comparison
equal deleted inserted replaced
1999:364cc624eb65 2000:39329372b667
248 } 248 }
249 }; 249 };
250 } 250 }
251 251
252 252
253 struct OrthancPlugins::PImpl 253 class OrthancPlugins::PImpl
254 { 254 {
255 private:
256 boost::mutex contextMutex_;
257 ServerContext* context_;
258
259
260 public:
255 class RestCallback : public boost::noncopyable 261 class RestCallback : public boost::noncopyable
256 { 262 {
257 private: 263 private:
258 boost::regex regex_; 264 boost::regex regex_;
259 OrthancPluginRestCallback callback_; 265 OrthancPluginRestCallback callback_;
299 } 305 }
300 } 306 }
301 }; 307 };
302 308
303 309
310 class ServerContextLock
311 {
312 private:
313 boost::mutex::scoped_lock lock_;
314 ServerContext* context_;
315
316 public:
317 ServerContextLock(PImpl& that) :
318 lock_(that.contextMutex_),
319 context_(that.context_)
320 {
321 if (context_ == NULL)
322 {
323 throw OrthancException(ErrorCode_DatabaseNotInitialized);
324 }
325 }
326
327 ServerContext& GetContext()
328 {
329 assert(context_ != NULL);
330 return *context_;
331 }
332 };
333
334
335 void SetServerContext(ServerContext* context)
336 {
337 boost::mutex::scoped_lock(contextMutex_);
338 context_ = context;
339 }
340
341
304 typedef std::pair<std::string, _OrthancPluginProperty> Property; 342 typedef std::pair<std::string, _OrthancPluginProperty> Property;
305 typedef std::list<RestCallback*> RestCallbacks; 343 typedef std::list<RestCallback*> RestCallbacks;
306 typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks; 344 typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks;
307 typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks; 345 typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks;
308 typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters; 346 typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters;
309 typedef std::map<Property, std::string> Properties; 347 typedef std::map<Property, std::string> Properties;
310 348
311 PluginsManager manager_; 349 PluginsManager manager_;
312 ServerContext* context_; 350
313 RestCallbacks restCallbacks_; 351 RestCallbacks restCallbacks_;
314 OnStoredCallbacks onStoredCallbacks_; 352 OnStoredCallbacks onStoredCallbacks_;
315 OnChangeCallbacks onChangeCallbacks_; 353 OnChangeCallbacks onChangeCallbacks_;
316 OrthancPluginFindCallback findCallback_; 354 OrthancPluginFindCallback findCallback_;
317 OrthancPluginWorklistCallback worklistCallback_; 355 OrthancPluginWorklistCallback worklistCallback_;
318 OrthancPluginDecodeImageCallback decodeImageCallback_; 356 OrthancPluginDecodeImageCallback decodeImageCallback_;
319 _OrthancPluginMoveCallback moveCallbacks_; 357 _OrthancPluginMoveCallback moveCallbacks_;
320 IncomingHttpRequestFilters incomingHttpRequestFilters_; 358 IncomingHttpRequestFilters incomingHttpRequestFilters_;
321 std::auto_ptr<StorageAreaFactory> storageArea_; 359 std::auto_ptr<StorageAreaFactory> storageArea_;
360
322 boost::recursive_mutex restCallbackMutex_; 361 boost::recursive_mutex restCallbackMutex_;
323 boost::recursive_mutex storedCallbackMutex_; 362 boost::recursive_mutex storedCallbackMutex_;
324 boost::recursive_mutex changeCallbackMutex_; 363 boost::recursive_mutex changeCallbackMutex_;
325 boost::mutex findCallbackMutex_; 364 boost::mutex findCallbackMutex_;
326 boost::mutex moveCallbackMutex_;
327 boost::mutex worklistCallbackMutex_; 365 boost::mutex worklistCallbackMutex_;
328 boost::mutex decodeImageCallbackMutex_; 366 boost::mutex decodeImageCallbackMutex_;
329 boost::recursive_mutex invokeServiceMutex_; 367 boost::recursive_mutex invokeServiceMutex_;
368
330 Properties properties_; 369 Properties properties_;
331 int argc_; 370 int argc_;
332 char** argv_; 371 char** argv_;
333 std::auto_ptr<OrthancPluginDatabase> database_; 372 std::auto_ptr<OrthancPluginDatabase> database_;
334 PluginsErrorDictionary dictionary_; 373 PluginsErrorDictionary dictionary_;
632 671
633 672
634 public: 673 public:
635 MoveHandler(OrthancPlugins& that) 674 MoveHandler(OrthancPlugins& that)
636 { 675 {
637 boost::mutex::scoped_lock lock(that.pimpl_->moveCallbackMutex_); 676 boost::recursive_mutex::scoped_lock lock(that.pimpl_->invokeServiceMutex_);
638 params_ = that.pimpl_->moveCallbacks_; 677 params_ = that.pimpl_->moveCallbacks_;
639 678
640 if (params_.callback == NULL || 679 if (params_.callback == NULL ||
641 params_.getMoveSize == NULL || 680 params_.getMoveSize == NULL ||
642 params_.applyMove == NULL || 681 params_.applyMove == NULL ||
730 } 769 }
731 770
732 771
733 void OrthancPlugins::SetServerContext(ServerContext& context) 772 void OrthancPlugins::SetServerContext(ServerContext& context)
734 { 773 {
735 pimpl_->context_ = &context; 774 pimpl_->SetServerContext(&context);
736 } 775 }
737 776
777
778 void OrthancPlugins::ResetServerContext()
779 {
780 pimpl_->SetServerContext(NULL);
781 }
738 782
739 783
740 OrthancPlugins::~OrthancPlugins() 784 OrthancPlugins::~OrthancPlugins()
741 { 785 {
742 for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin(); 786 for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin();
1033 } 1077 }
1034 1078
1035 1079
1036 void OrthancPlugins::RegisterMoveCallback(const void* parameters) 1080 void OrthancPlugins::RegisterMoveCallback(const void* parameters)
1037 { 1081 {
1082 // invokeServiceMutex_ is assumed to be locked
1083
1038 const _OrthancPluginMoveCallback& p = 1084 const _OrthancPluginMoveCallback& p =
1039 *reinterpret_cast<const _OrthancPluginMoveCallback*>(parameters); 1085 *reinterpret_cast<const _OrthancPluginMoveCallback*>(parameters);
1040
1041 boost::mutex::scoped_lock lock(pimpl_->moveCallbackMutex_);
1042 1086
1043 if (pimpl_->moveCallbacks_.callback != NULL) 1087 if (pimpl_->moveCallbacks_.callback != NULL)
1044 { 1088 {
1045 LOG(ERROR) << "Can only register one plugin to handle C-MOVE requests"; 1089 LOG(ERROR) << "Can only register one plugin to handle C-MOVE requests";
1046 throw OrthancException(ErrorCode_Plugin); 1090 throw OrthancException(ErrorCode_Plugin);
1230 1274
1231 translatedOutput->Answer(compressed); 1275 translatedOutput->Answer(compressed);
1232 } 1276 }
1233 1277
1234 1278
1235 void OrthancPlugins::CheckContextAvailable()
1236 {
1237 if (!pimpl_->context_)
1238 {
1239 throw OrthancException(ErrorCode_DatabaseNotInitialized);
1240 }
1241 }
1242
1243
1244 void OrthancPlugins::GetDicomForInstance(const void* parameters) 1279 void OrthancPlugins::GetDicomForInstance(const void* parameters)
1245 { 1280 {
1246 const _OrthancPluginGetDicomForInstance& p = 1281 const _OrthancPluginGetDicomForInstance& p =
1247 *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters); 1282 *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters);
1248 1283
1249 std::string dicom; 1284 std::string dicom;
1250 1285
1251 CheckContextAvailable(); 1286 {
1252 pimpl_->context_->ReadFile(dicom, p.instanceId, FileContentType_Dicom); 1287 PImpl::ServerContextLock lock(*pimpl_);
1288 lock.GetContext().ReadFile(dicom, p.instanceId, FileContentType_Dicom);
1289 }
1253 1290
1254 CopyToMemoryBuffer(*p.target, dicom); 1291 CopyToMemoryBuffer(*p.target, dicom);
1255 } 1292 }
1256 1293
1257 1294
1262 *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters); 1299 *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters);
1263 1300
1264 LOG(INFO) << "Plugin making REST GET call on URI " << p.uri 1301 LOG(INFO) << "Plugin making REST GET call on URI " << p.uri
1265 << (afterPlugins ? " (after plugins)" : " (built-in API)"); 1302 << (afterPlugins ? " (after plugins)" : " (built-in API)");
1266 1303
1267 CheckContextAvailable(); 1304 IHttpHandler* handler;
1268 IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); 1305
1306 {
1307 PImpl::ServerContextLock lock(*pimpl_);
1308 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
1309 }
1269 1310
1270 std::string result; 1311 std::string result;
1271 if (HttpToolbox::SimpleGet(result, handler, RequestOrigin_Plugins, p.uri)) 1312 if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri))
1272 { 1313 {
1273 CopyToMemoryBuffer(*p.target, result); 1314 CopyToMemoryBuffer(*p.target, result);
1274 } 1315 }
1275 else 1316 else
1276 { 1317 {
1294 std::string name(p.headersKeys[i]); 1335 std::string name(p.headersKeys[i]);
1295 std::transform(name.begin(), name.end(), name.begin(), ::tolower); 1336 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
1296 headers[name] = p.headersValues[i]; 1337 headers[name] = p.headersValues[i];
1297 } 1338 }
1298 1339
1299 CheckContextAvailable(); 1340 IHttpHandler* handler;
1300 IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins); 1341
1301 1342 {
1343 PImpl::ServerContextLock lock(*pimpl_);
1344 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins);
1345 }
1346
1302 std::string result; 1347 std::string result;
1303 if (HttpToolbox::SimpleGet(result, handler, RequestOrigin_Plugins, p.uri, headers)) 1348 if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri, headers))
1304 { 1349 {
1305 CopyToMemoryBuffer(*p.target, result); 1350 CopyToMemoryBuffer(*p.target, result);
1306 } 1351 }
1307 else 1352 else
1308 { 1353 {
1319 *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters); 1364 *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters);
1320 1365
1321 LOG(INFO) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put) 1366 LOG(INFO) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put)
1322 << " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)"); 1367 << " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)");
1323 1368
1324 CheckContextAvailable(); 1369 IHttpHandler* handler;
1325 IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); 1370
1326 1371 {
1372 PImpl::ServerContextLock lock(*pimpl_);
1373 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
1374 }
1375
1327 std::string result; 1376 std::string result;
1328 if (isPost ? 1377 if (isPost ?
1329 HttpToolbox::SimplePost(result, handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize) : 1378 HttpToolbox::SimplePost(result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize) :
1330 HttpToolbox::SimplePut (result, handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize)) 1379 HttpToolbox::SimplePut (result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize))
1331 { 1380 {
1332 CopyToMemoryBuffer(*p.target, result); 1381 CopyToMemoryBuffer(*p.target, result);
1333 } 1382 }
1334 else 1383 else
1335 { 1384 {
1343 { 1392 {
1344 const char* uri = reinterpret_cast<const char*>(parameters); 1393 const char* uri = reinterpret_cast<const char*>(parameters);
1345 LOG(INFO) << "Plugin making REST DELETE call on URI " << uri 1394 LOG(INFO) << "Plugin making REST DELETE call on URI " << uri
1346 << (afterPlugins ? " (after plugins)" : " (built-in API)"); 1395 << (afterPlugins ? " (after plugins)" : " (built-in API)");
1347 1396
1348 CheckContextAvailable(); 1397 IHttpHandler* handler;
1349 IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); 1398
1350 1399 {
1351 if (!HttpToolbox::SimpleDelete(handler, RequestOrigin_Plugins, uri)) 1400 PImpl::ServerContextLock lock(*pimpl_);
1401 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
1402 }
1403
1404 if (!HttpToolbox::SimpleDelete(*handler, RequestOrigin_Plugins, uri))
1352 { 1405 {
1353 throw OrthancException(ErrorCode_UnknownResource); 1406 throw OrthancException(ErrorCode_UnknownResource);
1354 } 1407 }
1355 } 1408 }
1356 1409
1399 1452
1400 default: 1453 default:
1401 throw OrthancException(ErrorCode_InternalError); 1454 throw OrthancException(ErrorCode_InternalError);
1402 } 1455 }
1403 1456
1404 CheckContextAvailable();
1405
1406 std::list<std::string> result; 1457 std::list<std::string> result;
1407 pimpl_->context_->GetIndex().LookupIdentifierExact(result, level, tag, p.argument); 1458
1459 {
1460 PImpl::ServerContextLock lock(*pimpl_);
1461 lock.GetContext().GetIndex().LookupIdentifierExact(result, level, tag, p.argument);
1462 }
1408 1463
1409 if (result.size() == 1) 1464 if (result.size() == 1)
1410 { 1465 {
1411 *p.result = CopyString(result.front()); 1466 *p.result = CopyString(result.front());
1412 } 1467 }
1860 { 1915 {
1861 throw OrthancException(ErrorCode_ParameterOutOfRange); 1916 throw OrthancException(ErrorCode_ParameterOutOfRange);
1862 } 1917 }
1863 1918
1864 std::string content; 1919 std::string content;
1865 pimpl_->context_->ReadFile(content, p.instanceId, FileContentType_Dicom); 1920
1921 {
1922 PImpl::ServerContextLock lock(*pimpl_);
1923 lock.GetContext().ReadFile(content, p.instanceId, FileContentType_Dicom);
1924 }
1925
1866 dicom.reset(new ParsedDicomFile(content)); 1926 dicom.reset(new ParsedDicomFile(content));
1867 } 1927 }
1868 1928
1869 Json::Value json; 1929 Json::Value json;
1870 dicom->ToJson(json, Plugins::Convert(p.format), 1930 dicom->ToJson(json, Plugins::Convert(p.format),
2072 } 2132 }
2073 } 2133 }
2074 2134
2075 2135
2076 2136
2077 bool OrthancPlugins::InvokeService(SharedLibrary& plugin, 2137 bool OrthancPlugins::InvokeSafeService(SharedLibrary& plugin,
2078 _OrthancPluginService service, 2138 _OrthancPluginService service,
2079 const void* parameters) 2139 const void* parameters)
2080 { 2140 {
2081 VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath(); 2141 // Services that can be run without mutual exclusion
2082
2083 if (service == _OrthancPluginService_DatabaseAnswer)
2084 {
2085 // This case solves a deadlock at (*) reported by James Webster
2086 // on 2015-10-27 that was present in versions of Orthanc <=
2087 // 0.9.4 and related to database plugins implementing a custom
2088 // index. The problem was that locking the database is already
2089 // ensured by the "ServerIndex" class if the invoked service is
2090 // "DatabaseAnswer".
2091 DatabaseAnswer(parameters);
2092 return true;
2093 }
2094
2095 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); // (*)
2096 2142
2097 switch (service) 2143 switch (service)
2098 { 2144 {
2099 case _OrthancPluginService_GetOrthancPath: 2145 case _OrthancPluginService_GetOrthancPath:
2100 { 2146 {
2126 return true; 2172 return true;
2127 } 2173 }
2128 2174
2129 case _OrthancPluginService_BufferCompression: 2175 case _OrthancPluginService_BufferCompression:
2130 BufferCompression(parameters); 2176 BufferCompression(parameters);
2131 return true;
2132
2133 case _OrthancPluginService_RegisterRestCallback:
2134 RegisterRestCallback(parameters, true);
2135 return true;
2136
2137 case _OrthancPluginService_RegisterRestCallbackNoLock:
2138 RegisterRestCallback(parameters, false);
2139 return true;
2140
2141 case _OrthancPluginService_RegisterOnStoredInstanceCallback:
2142 RegisterOnStoredInstanceCallback(parameters);
2143 return true;
2144
2145 case _OrthancPluginService_RegisterOnChangeCallback:
2146 RegisterOnChangeCallback(parameters);
2147 return true;
2148
2149 case _OrthancPluginService_RegisterWorklistCallback:
2150 RegisterWorklistCallback(parameters);
2151 return true;
2152
2153 case _OrthancPluginService_RegisterFindCallback:
2154 RegisterFindCallback(parameters);
2155 return true;
2156
2157 case _OrthancPluginService_RegisterMoveCallback:
2158 RegisterMoveCallback(parameters);
2159 return true;
2160
2161 case _OrthancPluginService_RegisterDecodeImageCallback:
2162 RegisterDecodeImageCallback(parameters);
2163 return true;
2164
2165 case _OrthancPluginService_RegisterIncomingHttpRequestFilter:
2166 RegisterIncomingHttpRequestFilter(parameters);
2167 return true; 2177 return true;
2168 2178
2169 case _OrthancPluginService_AnswerBuffer: 2179 case _OrthancPluginService_AnswerBuffer:
2170 AnswerBuffer(parameters); 2180 AnswerBuffer(parameters);
2171 return true; 2181 return true;
2263 case _OrthancPluginService_GetInstanceMetadata: 2273 case _OrthancPluginService_GetInstanceMetadata:
2264 case _OrthancPluginService_GetInstanceOrigin: 2274 case _OrthancPluginService_GetInstanceOrigin:
2265 AccessDicomInstance(service, parameters); 2275 AccessDicomInstance(service, parameters);
2266 return true; 2276 return true;
2267 2277
2268 case _OrthancPluginService_RegisterStorageArea:
2269 {
2270 LOG(INFO) << "Plugin has registered a custom storage area";
2271 const _OrthancPluginRegisterStorageArea& p =
2272 *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
2273
2274 if (pimpl_->storageArea_.get() == NULL)
2275 {
2276 pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
2277 }
2278 else
2279 {
2280 throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
2281 }
2282
2283 return true;
2284 }
2285
2286 case _OrthancPluginService_SetPluginProperty:
2287 {
2288 const _OrthancPluginSetPluginProperty& p =
2289 *reinterpret_cast<const _OrthancPluginSetPluginProperty*>(parameters);
2290 pimpl_->properties_[std::make_pair(p.plugin, p.property)] = p.value;
2291 return true;
2292 }
2293
2294 case _OrthancPluginService_SetGlobalProperty: 2278 case _OrthancPluginService_SetGlobalProperty:
2295 { 2279 {
2296 const _OrthancPluginGlobalProperty& p = 2280 const _OrthancPluginGlobalProperty& p =
2297 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); 2281 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
2298 if (p.property < 1024) 2282 if (p.property < 1024)
2299 { 2283 {
2300 return false; 2284 return false;
2301 } 2285 }
2302 else 2286 else
2303 { 2287 {
2304 CheckContextAvailable(); 2288 PImpl::ServerContextLock lock(*pimpl_);
2305 pimpl_->context_->GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value); 2289 lock.GetContext().GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
2306 return true; 2290 return true;
2307 } 2291 }
2308 } 2292 }
2309 2293
2310 case _OrthancPluginService_GetGlobalProperty: 2294 case _OrthancPluginService_GetGlobalProperty:
2311 { 2295 {
2312 CheckContextAvailable();
2313
2314 const _OrthancPluginGlobalProperty& p = 2296 const _OrthancPluginGlobalProperty& p =
2315 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); 2297 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
2316 std::string result = pimpl_->context_->GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value); 2298
2299 std::string result;
2300
2301 {
2302 PImpl::ServerContextLock lock(*pimpl_);
2303 result = lock.GetContext().GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
2304 }
2305
2317 *(p.result) = CopyString(result); 2306 *(p.result) = CopyString(result);
2318 return true; 2307 return true;
2319 } 2308 }
2320
2321 case _OrthancPluginService_GetCommandLineArgumentsCount:
2322 {
2323 const _OrthancPluginReturnSingleValue& p =
2324 *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
2325 *(p.resultUint32) = pimpl_->argc_ - 1;
2326 return true;
2327 }
2328
2329 case _OrthancPluginService_GetCommandLineArgument:
2330 {
2331 const _OrthancPluginGlobalProperty& p =
2332 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
2333
2334 if (p.property + 1 > pimpl_->argc_)
2335 {
2336 return false;
2337 }
2338 else
2339 {
2340 std::string arg = std::string(pimpl_->argv_[p.property + 1]);
2341 *(p.result) = CopyString(arg);
2342 return true;
2343 }
2344 }
2345
2346 case _OrthancPluginService_RegisterDatabaseBackend:
2347 {
2348 LOG(INFO) << "Plugin has registered a custom database back-end";
2349
2350 const _OrthancPluginRegisterDatabaseBackend& p =
2351 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
2352
2353 if (pimpl_->database_.get() == NULL)
2354 {
2355 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
2356 *p.backend, NULL, 0, p.payload));
2357 }
2358 else
2359 {
2360 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
2361 }
2362
2363 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
2364
2365 return true;
2366 }
2367
2368 case _OrthancPluginService_RegisterDatabaseBackendV2:
2369 {
2370 LOG(INFO) << "Plugin has registered a custom database back-end";
2371
2372 const _OrthancPluginRegisterDatabaseBackendV2& p =
2373 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
2374
2375 if (pimpl_->database_.get() == NULL)
2376 {
2377 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
2378 *p.backend, p.extensions,
2379 p.extensionsSize, p.payload));
2380 }
2381 else
2382 {
2383 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
2384 }
2385
2386 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
2387
2388 return true;
2389 }
2390
2391 case _OrthancPluginService_DatabaseAnswer:
2392 throw OrthancException(ErrorCode_InternalError); // Implemented before locking (*)
2393 2309
2394 case _OrthancPluginService_GetExpectedDatabaseVersion: 2310 case _OrthancPluginService_GetExpectedDatabaseVersion:
2395 { 2311 {
2396 const _OrthancPluginReturnSingleValue& p = 2312 const _OrthancPluginReturnSingleValue& p =
2397 *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters); 2313 *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
2556 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); 2472 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
2557 storage.Remove(p.uuid, Plugins::Convert(p.type)); 2473 storage.Remove(p.uuid, Plugins::Convert(p.type));
2558 return true; 2474 return true;
2559 } 2475 }
2560 2476
2477 case _OrthancPluginService_DicomBufferToJson:
2478 case _OrthancPluginService_DicomInstanceToJson:
2479 ApplyDicomToJson(service, parameters);
2480 return true;
2481
2482 case _OrthancPluginService_CreateDicom:
2483 ApplyCreateDicom(service, parameters);
2484 return true;
2485
2486 case _OrthancPluginService_WorklistAddAnswer:
2487 {
2488 const _OrthancPluginWorklistAnswersOperation& p =
2489 *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
2490 reinterpret_cast<const WorklistHandler*>(p.query)->AddAnswer(p.answers, p.dicom, p.size);
2491 return true;
2492 }
2493
2494 case _OrthancPluginService_WorklistMarkIncomplete:
2495 {
2496 const _OrthancPluginWorklistAnswersOperation& p =
2497 *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
2498 reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
2499 return true;
2500 }
2501
2502 case _OrthancPluginService_WorklistIsMatch:
2503 {
2504 const _OrthancPluginWorklistQueryOperation& p =
2505 *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
2506 *p.isMatch = reinterpret_cast<const WorklistHandler*>(p.query)->IsMatch(p.dicom, p.size);
2507 return true;
2508 }
2509
2510 case _OrthancPluginService_WorklistGetDicomQuery:
2511 {
2512 const _OrthancPluginWorklistQueryOperation& p =
2513 *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
2514 reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target);
2515 return true;
2516 }
2517
2518 case _OrthancPluginService_FindAddAnswer:
2519 {
2520 const _OrthancPluginFindOperation& p =
2521 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
2522 reinterpret_cast<DicomFindAnswers*>(p.answers)->Add(p.dicom, p.size);
2523 return true;
2524 }
2525
2526 case _OrthancPluginService_FindMarkIncomplete:
2527 {
2528 const _OrthancPluginFindOperation& p =
2529 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
2530 reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
2531 return true;
2532 }
2533
2534 case _OrthancPluginService_GetFindQuerySize:
2535 case _OrthancPluginService_GetFindQueryTag:
2536 case _OrthancPluginService_GetFindQueryTagName:
2537 case _OrthancPluginService_GetFindQueryValue:
2538 {
2539 const _OrthancPluginFindOperation& p =
2540 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
2541 reinterpret_cast<const FindHandler*>(p.query)->Invoke(service, p);
2542 return true;
2543 }
2544
2545 case _OrthancPluginService_CreateImage:
2546 case _OrthancPluginService_CreateImageAccessor:
2547 case _OrthancPluginService_DecodeDicomImage:
2548 ApplyCreateImage(service, parameters);
2549 return true;
2550
2551 case _OrthancPluginService_ComputeMd5:
2552 case _OrthancPluginService_ComputeSha1:
2553 ComputeHash(service, parameters);
2554 return true;
2555
2556 case _OrthancPluginService_LookupDictionary:
2557 ApplyLookupDictionary(parameters);
2558 return true;
2559
2560 case _OrthancPluginService_GenerateUuid:
2561 {
2562 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result =
2563 CopyString(Toolbox::GenerateUuid());
2564 return true;
2565 }
2566
2567 default:
2568 return false;
2569 }
2570 }
2571
2572
2573
2574 bool OrthancPlugins::InvokeProtectedService(SharedLibrary& plugin,
2575 _OrthancPluginService service,
2576 const void* parameters)
2577 {
2578 // Services that must be run in mutual exclusion. Guideline:
2579 // Whenever "pimpl_" is directly accessed by the service, it
2580 // should be listed here.
2581
2582 switch (service)
2583 {
2584 case _OrthancPluginService_RegisterRestCallback:
2585 RegisterRestCallback(parameters, true);
2586 return true;
2587
2588 case _OrthancPluginService_RegisterRestCallbackNoLock:
2589 RegisterRestCallback(parameters, false);
2590 return true;
2591
2592 case _OrthancPluginService_RegisterOnStoredInstanceCallback:
2593 RegisterOnStoredInstanceCallback(parameters);
2594 return true;
2595
2596 case _OrthancPluginService_RegisterOnChangeCallback:
2597 RegisterOnChangeCallback(parameters);
2598 return true;
2599
2600 case _OrthancPluginService_RegisterWorklistCallback:
2601 RegisterWorklistCallback(parameters);
2602 return true;
2603
2604 case _OrthancPluginService_RegisterFindCallback:
2605 RegisterFindCallback(parameters);
2606 return true;
2607
2608 case _OrthancPluginService_RegisterMoveCallback:
2609 RegisterMoveCallback(parameters);
2610 return true;
2611
2612 case _OrthancPluginService_RegisterDecodeImageCallback:
2613 RegisterDecodeImageCallback(parameters);
2614 return true;
2615
2616 case _OrthancPluginService_RegisterIncomingHttpRequestFilter:
2617 RegisterIncomingHttpRequestFilter(parameters);
2618 return true;
2619
2620 case _OrthancPluginService_RegisterStorageArea:
2621 {
2622 LOG(INFO) << "Plugin has registered a custom storage area";
2623 const _OrthancPluginRegisterStorageArea& p =
2624 *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
2625
2626 if (pimpl_->storageArea_.get() == NULL)
2627 {
2628 pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
2629 }
2630 else
2631 {
2632 throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
2633 }
2634
2635 return true;
2636 }
2637
2638 case _OrthancPluginService_SetPluginProperty:
2639 {
2640 const _OrthancPluginSetPluginProperty& p =
2641 *reinterpret_cast<const _OrthancPluginSetPluginProperty*>(parameters);
2642 pimpl_->properties_[std::make_pair(p.plugin, p.property)] = p.value;
2643 return true;
2644 }
2645
2646 case _OrthancPluginService_GetCommandLineArgumentsCount:
2647 {
2648 const _OrthancPluginReturnSingleValue& p =
2649 *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
2650 *(p.resultUint32) = pimpl_->argc_ - 1;
2651 return true;
2652 }
2653
2654 case _OrthancPluginService_GetCommandLineArgument:
2655 {
2656 const _OrthancPluginGlobalProperty& p =
2657 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
2658
2659 if (p.property + 1 > pimpl_->argc_)
2660 {
2661 return false;
2662 }
2663 else
2664 {
2665 std::string arg = std::string(pimpl_->argv_[p.property + 1]);
2666 *(p.result) = CopyString(arg);
2667 return true;
2668 }
2669 }
2670
2671 case _OrthancPluginService_RegisterDatabaseBackend:
2672 {
2673 LOG(INFO) << "Plugin has registered a custom database back-end";
2674
2675 const _OrthancPluginRegisterDatabaseBackend& p =
2676 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
2677
2678 if (pimpl_->database_.get() == NULL)
2679 {
2680 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
2681 *p.backend, NULL, 0, p.payload));
2682 }
2683 else
2684 {
2685 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
2686 }
2687
2688 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
2689
2690 return true;
2691 }
2692
2693 case _OrthancPluginService_RegisterDatabaseBackendV2:
2694 {
2695 LOG(INFO) << "Plugin has registered a custom database back-end";
2696
2697 const _OrthancPluginRegisterDatabaseBackendV2& p =
2698 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
2699
2700 if (pimpl_->database_.get() == NULL)
2701 {
2702 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
2703 *p.backend, p.extensions,
2704 p.extensionsSize, p.payload));
2705 }
2706 else
2707 {
2708 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
2709 }
2710
2711 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
2712
2713 return true;
2714 }
2715
2716 case _OrthancPluginService_DatabaseAnswer:
2717 throw OrthancException(ErrorCode_InternalError); // Implemented before locking (*)
2718
2561 case _OrthancPluginService_RegisterErrorCode: 2719 case _OrthancPluginService_RegisterErrorCode:
2562 { 2720 {
2563 const _OrthancPluginRegisterErrorCode& p = 2721 const _OrthancPluginRegisterErrorCode& p =
2564 *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters); 2722 *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters);
2565 *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message); 2723 *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message);
2591 Toolbox::ReconstructMainDicomTags(*pimpl_->database_, storage, Plugins::Convert(p.level)); 2749 Toolbox::ReconstructMainDicomTags(*pimpl_->database_, storage, Plugins::Convert(p.level));
2592 2750
2593 return true; 2751 return true;
2594 } 2752 }
2595 2753
2596 case _OrthancPluginService_DicomBufferToJson:
2597 case _OrthancPluginService_DicomInstanceToJson:
2598 ApplyDicomToJson(service, parameters);
2599 return true;
2600
2601 case _OrthancPluginService_CreateDicom:
2602 ApplyCreateDicom(service, parameters);
2603 return true;
2604
2605 case _OrthancPluginService_WorklistAddAnswer:
2606 {
2607 const _OrthancPluginWorklistAnswersOperation& p =
2608 *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
2609 reinterpret_cast<const WorklistHandler*>(p.query)->AddAnswer(p.answers, p.dicom, p.size);
2610 return true;
2611 }
2612
2613 case _OrthancPluginService_WorklistMarkIncomplete:
2614 {
2615 const _OrthancPluginWorklistAnswersOperation& p =
2616 *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
2617 reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
2618 return true;
2619 }
2620
2621 case _OrthancPluginService_WorklistIsMatch:
2622 {
2623 const _OrthancPluginWorklistQueryOperation& p =
2624 *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
2625 *p.isMatch = reinterpret_cast<const WorklistHandler*>(p.query)->IsMatch(p.dicom, p.size);
2626 return true;
2627 }
2628
2629 case _OrthancPluginService_WorklistGetDicomQuery:
2630 {
2631 const _OrthancPluginWorklistQueryOperation& p =
2632 *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
2633 reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target);
2634 return true;
2635 }
2636
2637 case _OrthancPluginService_FindAddAnswer:
2638 {
2639 const _OrthancPluginFindOperation& p =
2640 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
2641 reinterpret_cast<DicomFindAnswers*>(p.answers)->Add(p.dicom, p.size);
2642 return true;
2643 }
2644
2645 case _OrthancPluginService_FindMarkIncomplete:
2646 {
2647 const _OrthancPluginFindOperation& p =
2648 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
2649 reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
2650 return true;
2651 }
2652
2653 case _OrthancPluginService_GetFindQuerySize:
2654 case _OrthancPluginService_GetFindQueryTag:
2655 case _OrthancPluginService_GetFindQueryTagName:
2656 case _OrthancPluginService_GetFindQueryValue:
2657 {
2658 const _OrthancPluginFindOperation& p =
2659 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
2660 reinterpret_cast<const FindHandler*>(p.query)->Invoke(service, p);
2661 return true;
2662 }
2663
2664 case _OrthancPluginService_CreateImage:
2665 case _OrthancPluginService_CreateImageAccessor:
2666 case _OrthancPluginService_DecodeDicomImage:
2667 ApplyCreateImage(service, parameters);
2668 return true;
2669
2670 case _OrthancPluginService_ComputeMd5:
2671 case _OrthancPluginService_ComputeSha1:
2672 ComputeHash(service, parameters);
2673 return true;
2674
2675 case _OrthancPluginService_LookupDictionary:
2676 ApplyLookupDictionary(parameters);
2677 return true;
2678
2679 case _OrthancPluginService_GenerateUuid:
2680 {
2681 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result =
2682 CopyString(Toolbox::GenerateUuid());
2683 return true;
2684 }
2685
2686 default: 2754 default:
2687 { 2755 {
2688 // This service is unknown to the Orthanc plugin engine 2756 // This service is unknown to the Orthanc plugin engine
2689 return false; 2757 return false;
2690 } 2758 }
2691 } 2759 }
2692 } 2760 }
2693 2761
2694 2762
2763
2764 bool OrthancPlugins::InvokeService(SharedLibrary& plugin,
2765 _OrthancPluginService service,
2766 const void* parameters)
2767 {
2768 VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath();
2769
2770 if (service == _OrthancPluginService_DatabaseAnswer)
2771 {
2772 // This case solves a deadlock at (*) reported by James Webster
2773 // on 2015-10-27 that was present in versions of Orthanc <=
2774 // 0.9.4 and related to database plugins implementing a custom
2775 // index. The problem was that locking the database is already
2776 // ensured by the "ServerIndex" class if the invoked service is
2777 // "DatabaseAnswer".
2778 DatabaseAnswer(parameters);
2779 return true;
2780 }
2781
2782 if (InvokeSafeService(plugin, service, parameters))
2783 {
2784 // The invoked service does not require locking
2785 return true;
2786 }
2787 else
2788 {
2789 // The invoked service requires locking
2790 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); // (*)
2791 return InvokeProtectedService(plugin, service, parameters);
2792 }
2793 }
2794
2795
2695 bool OrthancPlugins::HasStorageArea() const 2796 bool OrthancPlugins::HasStorageArea() const
2696 { 2797 {
2798 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
2697 return pimpl_->storageArea_.get() != NULL; 2799 return pimpl_->storageArea_.get() != NULL;
2698 } 2800 }
2699 2801
2700 bool OrthancPlugins::HasDatabaseBackend() const 2802 bool OrthancPlugins::HasDatabaseBackend() const
2701 { 2803 {
2804 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
2702 return pimpl_->database_.get() != NULL; 2805 return pimpl_->database_.get() != NULL;
2703 } 2806 }
2704 2807
2705 2808
2706 IStorageArea* OrthancPlugins::CreateStorageArea() 2809 IStorageArea* OrthancPlugins::CreateStorageArea()
2855 } 2958 }
2856 2959
2857 2960
2858 bool OrthancPlugins::HasMoveHandler() 2961 bool OrthancPlugins::HasMoveHandler()
2859 { 2962 {
2860 boost::mutex::scoped_lock lock(pimpl_->moveCallbackMutex_); 2963 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
2861 return pimpl_->moveCallbacks_.callback != NULL; 2964 return pimpl_->moveCallbacks_.callback != NULL;
2862 } 2965 }
2863 2966
2864 2967
2865 bool OrthancPlugins::HasCustomImageDecoder() 2968 bool OrthancPlugins::HasCustomImageDecoder()