vscode: add Cygwin terminal profile and config for VS debugger for Windows
[LibreOffice.git] / jvmfwk / source / framework.cxx
blob5f83e7be739e7d7596ebfbb7eed9b5c25c62ea86
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <cassert>
24 #include <memory>
26 #include <rtl/ref.hxx>
27 #include <rtl/ustring.hxx>
28 #include <osl/diagnose.h>
29 #ifdef _WIN32
30 #include <osl/file.hxx>
31 #include <osl/process.h>
32 #endif
33 #include <osl/thread.hxx>
34 #include <jvmfwk/framework.hxx>
35 #include <vendorbase.hxx>
36 #include <vendorplugin.hxx>
37 #include <vector>
38 #include <algorithm>
39 #include "framework.hxx"
40 #include <fwkutil.hxx>
41 #include <elements.hxx>
42 #include <fwkbase.hxx>
44 namespace {
46 bool g_bEnabledSwitchedOn = false;
48 JavaVM * g_pJavaVM = nullptr;
50 bool areEqualJavaInfo(
51 JavaInfo const * pInfoA,JavaInfo const * pInfoB)
53 return jfw_areEqualJavaInfo(pInfoA, pInfoB);
58 javaFrameworkError jfw_findAllJREs(std::vector<std::unique_ptr<JavaInfo>> *pparInfo)
60 assert(pparInfo != nullptr);
61 try
63 osl::MutexGuard guard(jfw::FwkMutex());
65 jfw::VendorSettings aVendorSettings;
66 std::vector<std::unique_ptr<JavaInfo>> vecInfo;
68 //Use all plug-in libraries to get Java installations.
69 std::vector<std::unique_ptr<JavaInfo>> arInfos;
70 std::vector<rtl::Reference<jfw_plugin::VendorBase>> infos;
71 javaPluginError plerr = jfw_plugin_getAllJavaInfos(
72 true,
73 aVendorSettings,
74 & arInfos,
75 infos);
77 if (plerr != javaPluginError::NONE)
78 return JFW_E_ERROR;
80 for (auto & j: arInfos)
81 vecInfo.push_back(std::move(j));
83 // direct mode disregards Java settings, so only retrieve
84 // JREs from settings when application mode is used
85 if (jfw::getMode() == jfw::JFW_MODE_APPLICATION)
87 //get the list of paths to jre locations which have been
88 //added manually
89 const jfw::MergedSettings settings;
90 const std::vector<OUString> vecJRELocations =
91 settings.getJRELocations();
92 //Check if any plugin can detect JREs at the location
93 // of the paths added by jfw_addJRELocation
94 //Check every manually added location
95 for (auto const & ii: vecJRELocations)
97 std::unique_ptr<JavaInfo> aInfo;
98 plerr = jfw_plugin_getJavaInfoByPath(
99 ii,
100 aVendorSettings,
101 &aInfo);
102 if (plerr == javaPluginError::NoJre)
103 continue;
104 if (plerr == javaPluginError::FailedVersion)
105 continue;
106 if (plerr == javaPluginError::WrongArch)
107 continue;
108 else if (plerr != javaPluginError::NONE)
109 return JFW_E_ERROR;
111 // Was this JRE already added?
112 if (std::none_of(
113 vecInfo.begin(), vecInfo.end(),
114 [&aInfo](std::unique_ptr<JavaInfo> const & info) {
115 return areEqualJavaInfo(
116 info.get(), aInfo.get());
119 vecInfo.push_back(std::move(aInfo));
124 *pparInfo = std::move(vecInfo);
126 return JFW_E_NONE;
128 catch (const jfw::FrameworkException& e)
130 SAL_WARN( "jfw", e.message);
131 return e.errorCode;
135 javaFrameworkError jfw_startVM(
136 JavaInfo const * pInfo, std::vector<OUString> const & arOptions,
137 JavaVM ** ppVM, JNIEnv ** ppEnv)
139 assert(ppVM != nullptr);
140 javaFrameworkError errcode = JFW_E_NONE;
144 osl::MutexGuard guard(jfw::FwkMutex());
146 //We keep this pointer so we can determine if a VM has already
147 //been created.
148 if (g_pJavaVM != nullptr)
149 return JFW_E_RUNNING_JVM;
151 std::vector<OString> vmParams;
152 OString sUserClassPath;
153 std::unique_ptr<JavaInfo> aInfo;
154 if (pInfo == nullptr)
156 jfw::JFW_MODE mode = jfw::getMode();
157 if (mode == jfw::JFW_MODE_APPLICATION)
159 const jfw::MergedSettings settings;
160 if (!settings.getEnabled())
161 return JFW_E_JAVA_DISABLED;
162 aInfo = settings.createJavaInfo();
163 //check if a Java has ever been selected
164 if (!aInfo)
165 return JFW_E_NO_SELECT;
167 //check if the javavendors.xml has changed after a Java was selected
168 OString sVendorUpdate = jfw::getElementUpdated();
170 if (sVendorUpdate != settings.getJavaInfoAttrVendorUpdate())
171 return JFW_E_INVALID_SETTINGS;
173 //check if JAVA is disabled
174 //If Java is enabled, but it was disabled when this process was started
175 // then no preparational work, such as setting the LD_LIBRARY_PATH, was
176 //done. Therefore if a JRE needs it, it must not be started.
177 if (g_bEnabledSwitchedOn &&
178 (aInfo->nRequirements & JFW_REQUIRE_NEEDRESTART))
179 return JFW_E_NEED_RESTART;
181 //Check if the selected Java was set in this process. If so it
182 //must not have the requirements flag JFW_REQUIRE_NEEDRESTART
183 if ((aInfo->nRequirements & JFW_REQUIRE_NEEDRESTART)
184 && jfw::wasJavaSelectedInSameProcess())
185 return JFW_E_NEED_RESTART;
187 vmParams = settings.getVmParametersUtf8();
188 sUserClassPath = jfw::makeClassPathOption(settings.getUserClassPath());
189 } // end mode FWK_MODE_OFFICE
190 else if (mode == jfw::JFW_MODE_DIRECT)
192 errcode = jfw_getSelectedJRE(&aInfo);
193 if (errcode != JFW_E_NONE)
194 return errcode;
195 //In direct mode the options are specified by bootstrap variables
196 //of the form UNO_JAVA_JFW_PARAMETER_1 .. UNO_JAVA_JFW_PARAMETER_n
197 vmParams = jfw::BootParams::getVMParameters();
198 auto const cp = jfw::BootParams::getClasspath();
199 if (!cp.isEmpty())
201 sUserClassPath =
202 "-Djava.class.path=" + cp;
205 else
206 OSL_ASSERT(false);
207 pInfo = aInfo.get();
209 assert(pInfo != nullptr);
211 #ifdef _WIN32
212 // Alternative JREs (AdoptOpenJDK, Azul Zulu) are missing the bin/ folder in
213 // java.library.path. Somehow setting java.library.path accordingly doesn't work,
214 // but the PATH gets picked up, so add it there.
215 // Without this hack, some features don't work in alternative JREs.
216 OUString sPATH;
217 osl_getEnvironment(OUString("PATH").pData, &sPATH.pData);
218 OUString sJRELocation;
219 osl::FileBase::getSystemPathFromFileURL(pInfo->sLocation + "/bin", sJRELocation);
220 if (sPATH.isEmpty())
221 sPATH = sJRELocation;
222 else
223 sPATH = sJRELocation + OUStringChar(SAL_PATHSEPARATOR) + sPATH;
224 osl_setEnvironment(OUString("PATH").pData, sPATH.pData);
225 #endif // _WIN32
227 // create JavaVMOptions array that is passed to the plugin
228 // it contains the classpath and all options set in the
229 //options dialog
230 std::unique_ptr<JavaVMOption[]> sarJOptions(
231 new JavaVMOption[
232 arOptions.size() + (sUserClassPath.isEmpty() ? 2 : 3) + vmParams.size()]);
233 JavaVMOption * arOpt = sarJOptions.get();
234 if (! arOpt)
235 return JFW_E_ERROR;
237 //The first argument is the classpath
238 int index = 0;
239 if (!sUserClassPath.isEmpty()) {
240 arOpt[index].optionString= const_cast<char*>(sUserClassPath.getStr());
241 arOpt[index].extraInfo = nullptr;
242 ++index;
244 // Set a flag that this JVM has been created via the JNI Invocation API
245 // (used, for example, by UNO remote bridges to share a common thread pool
246 // factory among Java and native bridge implementations):
247 arOpt[index].optionString = const_cast<char *>("-Dorg.openoffice.native=");
248 arOpt[index].extraInfo = nullptr;
249 ++index;
251 // Don't intercept SIGTERM
252 arOpt[index].optionString = const_cast<char *>("-Xrs");
253 arOpt[index].extraInfo = nullptr;
254 ++index;
256 //add the options set by options dialog
257 for (auto const & vmParam : vmParams)
259 arOpt[index].optionString = const_cast<char*>(vmParam.getStr());
260 arOpt[index].extraInfo = nullptr;
261 index ++;
263 //add all options of the arOptions argument
264 std::vector<OString> convertedOptions;
265 for (auto const & ii: arOptions)
267 OString conv = OUStringToOString(ii, osl_getThreadTextEncoding());
268 convertedOptions.push_back(conv);
269 // keep conv.getStr() alive until after the call to
270 // jfw_plugin_startJavaVirtualMachine below
271 arOpt[index].optionString = const_cast<char *>(conv.getStr());
272 arOpt[index].extraInfo = nullptr;
273 index++;
276 //start Java
277 JavaVM *pVm = nullptr;
278 SAL_INFO("jfw", "Starting Java");
279 javaPluginError plerr = jfw_plugin_startJavaVirtualMachine(pInfo, arOpt, index, & pVm, ppEnv);
280 if (plerr == javaPluginError::VmCreationFailed)
282 errcode = JFW_E_VM_CREATION_FAILED;
284 else if (plerr != javaPluginError::NONE )
286 errcode = JFW_E_ERROR;
288 else
290 g_pJavaVM = pVm;
291 *ppVM = pVm;
294 catch (const jfw::FrameworkException& e)
296 errcode = e.errorCode;
297 SAL_WARN( "jfw", e.message);
300 return errcode;
303 /** We do not use here jfw_findAllJREs and then check if a JavaInfo
304 meets the requirements, because that means using all plug-ins, which
305 may take quite a while. The implementation first inspects JAVA_HOME and
306 PATH environment variables. If no suitable JavaInfo is found there, it
307 inspects all JavaInfos found by the jfw_plugin_get* functions.
309 javaFrameworkError jfw_findAndSelectJRE(std::unique_ptr<JavaInfo> *pInfo)
311 javaFrameworkError errcode = JFW_E_NONE;
314 osl::MutexGuard guard(jfw::FwkMutex());
315 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
316 return JFW_E_DIRECT_MODE;
317 std::unique_ptr<JavaInfo> aCurrentInfo;
320 // 'bInfoFound' indicates whether a Java installation has been found
321 bool bInfoFound = false;
323 // get list of vendors for Java installations
324 jfw::VendorSettings aVendorSettings;
326 std::vector<rtl::Reference<jfw_plugin::VendorBase>> infos;
328 // first inspect Java installation that the JAVA_HOME
329 // environment variable points to (if it is set)
330 if (jfw_plugin_getJavaInfoFromJavaHome(
331 aVendorSettings, &aCurrentInfo, infos)
332 == javaPluginError::NONE)
334 bInfoFound = true;
337 // if no Java installation was detected by using JAVA_HOME,
338 // query PATH for Java installations
339 if (!bInfoFound)
341 std::vector<std::unique_ptr<JavaInfo>> vecJavaInfosFromPath;
342 if (jfw_plugin_getJavaInfosFromPath(
343 aVendorSettings, vecJavaInfosFromPath, infos)
344 == javaPluginError::NONE)
346 assert(!vecJavaInfosFromPath.empty());
347 aCurrentInfo = std::move(vecJavaInfosFromPath[0]);
348 bInfoFound = true;
353 // if no suitable Java installation has been found yet:
354 // first use jfw_plugin_getAllJavaInfos to find a suitable Java installation,
355 // then try paths that have been added manually
356 if (!bInfoFound)
358 //get all installations
359 std::vector<std::unique_ptr<JavaInfo>> arInfos;
360 javaPluginError plerr = jfw_plugin_getAllJavaInfos(
361 false,
362 aVendorSettings,
363 & arInfos,
364 infos);
366 if (plerr == javaPluginError::NONE && !arInfos.empty())
368 aCurrentInfo = std::move(arInfos[0]);
371 if (!aCurrentInfo)
372 {//The plug-ins did not find a suitable Java. Now try the paths which have been
373 //added manually.
374 //get the list of paths to jre locations which have been added manually
375 const jfw::MergedSettings settings;
376 //node.loadFromSettings();
377 const std::vector<OUString> & vecJRELocations =
378 settings.getJRELocations();
379 //use all plug-ins to determine the JavaInfo objects
380 for (auto const & JRELocation : vecJRELocations)
382 std::unique_ptr<JavaInfo> aInfo;
383 javaPluginError err = jfw_plugin_getJavaInfoByPath(
384 JRELocation,
385 aVendorSettings,
386 &aInfo);
387 if (err == javaPluginError::NoJre)
388 continue;
389 if (err == javaPluginError::FailedVersion)
390 continue;
391 else if (err !=javaPluginError::NONE)
392 return JFW_E_ERROR;
394 if (aInfo)
396 aCurrentInfo = std::move(aInfo);
397 break;
399 }//end iterate over paths
402 if (aCurrentInfo)
404 jfw::NodeJava javaNode(jfw::NodeJava::USER);
405 javaNode.setJavaInfo(aCurrentInfo.get(),true);
406 javaNode.write();
407 //remember that this JRE was selected in this process
408 jfw::setJavaSelected();
410 if (pInfo !=nullptr)
412 *pInfo = std::move(aCurrentInfo);
415 else
417 errcode = JFW_E_NO_JAVA_FOUND;
420 catch (const jfw::FrameworkException& e)
422 errcode = e.errorCode;
423 SAL_WARN( "jfw", e.message );
426 return errcode;
429 bool jfw_areEqualJavaInfo(JavaInfo const * pInfoA,JavaInfo const * pInfoB)
431 if (pInfoA == pInfoB)
432 return true;
433 if (pInfoA == nullptr || pInfoB == nullptr)
434 return false;
435 if (pInfoA->sVendor == pInfoB->sVendor
436 && pInfoA->sLocation == pInfoB->sLocation
437 && pInfoA->sVersion == pInfoB->sVersion
438 && pInfoA->nRequirements == pInfoB->nRequirements
439 && pInfoA->arVendorData == pInfoB->arVendorData)
441 return true;
443 return false;
446 javaFrameworkError jfw_getSelectedJRE(std::unique_ptr<JavaInfo> *ppInfo)
448 assert(ppInfo != nullptr);
449 javaFrameworkError errcode = JFW_E_NONE;
452 osl::MutexGuard guard(jfw::FwkMutex());
454 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
456 if ((errcode = jfw_getJavaInfoByPath(
457 jfw::BootParams::getJREHome(), ppInfo))
458 != JFW_E_NONE)
459 throw jfw::FrameworkException(
460 JFW_E_CONFIGURATION,
461 "[Java framework] The JRE specified by the bootstrap "
462 "variable UNO_JAVA_JFW_JREHOME or UNO_JAVA_JFW_ENV_JREHOME "
463 " could not be recognized. Check the values and make sure that you "
464 "use a plug-in library that can recognize that JRE.");
466 return JFW_E_NONE;
469 const jfw::MergedSettings settings;
470 *ppInfo = settings.createJavaInfo();
471 if (!*ppInfo)
473 return JFW_E_NONE;
475 //If the javavendors.xml has changed, then the current selected
476 //Java is not valid anymore
477 // /java/javaInfo/@vendorUpdate != javaSelection/updated (javavendors.xml)
478 OString sUpdated = jfw::getElementUpdated();
480 if (sUpdated != settings.getJavaInfoAttrVendorUpdate())
482 ppInfo->reset();
483 return JFW_E_INVALID_SETTINGS;
486 catch (const jfw::FrameworkException& e)
488 errcode = e.errorCode;
489 SAL_WARN( "jfw", e.message );
491 return errcode;
494 bool jfw_isVMRunning()
496 osl::MutexGuard guard(jfw::FwkMutex());
497 return g_pJavaVM != nullptr;
500 javaFrameworkError jfw_getJavaInfoByPath(OUString const & pPath, std::unique_ptr<JavaInfo> *ppInfo)
502 assert(ppInfo != nullptr);
503 javaFrameworkError errcode = JFW_E_NONE;
506 osl::MutexGuard guard(jfw::FwkMutex());
508 jfw::VendorSettings aVendorSettings;
510 //ask all plugins if this is a JRE.
511 //If so check if it meets the version requirements.
512 //Only if it does return a JavaInfo
513 javaPluginError plerr = jfw_plugin_getJavaInfoByPath(
514 pPath,
515 aVendorSettings,
516 ppInfo);
518 if(plerr == javaPluginError::FailedVersion)
519 {//found JRE but it has the wrong version
520 ppInfo->reset();
521 errcode = JFW_E_FAILED_VERSION;
523 OSL_ASSERT(plerr == javaPluginError::NONE || plerr == javaPluginError::NoJre);
524 if (!*ppInfo && errcode != JFW_E_FAILED_VERSION)
525 errcode = JFW_E_NOT_RECOGNIZED;
527 catch (const jfw::FrameworkException& e)
529 errcode = e.errorCode;
530 SAL_WARN( "jfw", e.message );
533 return errcode;
537 javaFrameworkError jfw_setSelectedJRE(JavaInfo const *pInfo)
539 javaFrameworkError errcode = JFW_E_NONE;
542 osl::MutexGuard guard(jfw::FwkMutex());
543 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
544 return JFW_E_DIRECT_MODE;
545 //check if pInfo is the selected JRE
546 std::unique_ptr<JavaInfo> currentInfo;
547 errcode = jfw_getSelectedJRE( & currentInfo);
548 if (errcode != JFW_E_NONE && errcode != JFW_E_INVALID_SETTINGS)
549 return errcode;
551 if (!jfw_areEqualJavaInfo(currentInfo.get(), pInfo))
553 jfw::NodeJava node(jfw::NodeJava::USER);
554 node.setJavaInfo(pInfo, false);
555 node.write();
556 //remember that the JRE was selected in this process
557 jfw::setJavaSelected();
560 catch (const jfw::FrameworkException& e)
562 errcode = e.errorCode;
563 SAL_WARN( "jfw", e.message );
565 return errcode;
567 javaFrameworkError jfw_setEnabled(bool bEnabled)
569 javaFrameworkError errcode = JFW_E_NONE;
572 osl::MutexGuard guard(jfw::FwkMutex());
573 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
574 return JFW_E_DIRECT_MODE;
576 if (!g_bEnabledSwitchedOn && bEnabled)
578 //When the process started then Enabled was false.
579 //This is first time enabled is set to true.
580 //That means, no preparational work has been done, such as setting the
581 //LD_LIBRARY_PATH, etc.
583 //check if Enabled is false;
584 const jfw::MergedSettings settings;
585 if (!settings.getEnabled())
586 g_bEnabledSwitchedOn = true;
588 jfw::NodeJava node(jfw::NodeJava::USER);
589 node.setEnabled(bEnabled);
590 node.write();
592 catch (const jfw::FrameworkException& e)
594 errcode = e.errorCode;
595 SAL_WARN( "jfw", e.message );
597 return errcode;
600 javaFrameworkError jfw_getEnabled(bool *pbEnabled)
602 assert(pbEnabled != nullptr);
603 javaFrameworkError errcode = JFW_E_NONE;
606 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
607 return JFW_E_DIRECT_MODE;
608 osl::MutexGuard guard(jfw::FwkMutex());
609 jfw::MergedSettings settings;
610 *pbEnabled = settings.getEnabled();
612 catch (const jfw::FrameworkException& e)
614 errcode = e.errorCode;
615 SAL_WARN( "jfw", e.message );
617 return errcode;
621 javaFrameworkError jfw_setVMParameters(std::vector<OUString> const & arOptions)
623 javaFrameworkError errcode = JFW_E_NONE;
626 osl::MutexGuard guard(jfw::FwkMutex());
627 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
628 return JFW_E_DIRECT_MODE;
629 jfw::NodeJava node(jfw::NodeJava::USER);
630 node.setVmParameters(arOptions);
631 node.write();
633 catch (const jfw::FrameworkException& e)
635 errcode = e.errorCode;
636 SAL_WARN( "jfw", e.message );
639 return errcode;
642 javaFrameworkError jfw_getVMParameters(std::vector<OUString> * parOptions)
644 javaFrameworkError errcode = JFW_E_NONE;
647 osl::MutexGuard guard(jfw::FwkMutex());
648 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
649 return JFW_E_DIRECT_MODE;
651 const jfw::MergedSettings settings;
652 settings.getVmParametersArray(parOptions);
654 catch (const jfw::FrameworkException& e)
656 errcode = e.errorCode;
657 SAL_WARN( "jfw", e.message );
659 return errcode;
662 javaFrameworkError jfw_setUserClassPath(OUString const & pCp)
664 javaFrameworkError errcode = JFW_E_NONE;
667 osl::MutexGuard guard(jfw::FwkMutex());
668 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
669 return JFW_E_DIRECT_MODE;
670 jfw::NodeJava node(jfw::NodeJava::USER);
671 node.setUserClassPath(pCp);
672 node.write();
674 catch (const jfw::FrameworkException& e)
676 errcode = e.errorCode;
677 SAL_WARN( "jfw", e.message );
679 return errcode;
682 javaFrameworkError jfw_getUserClassPath(OUString * ppCP)
684 assert(ppCP != nullptr);
685 javaFrameworkError errcode = JFW_E_NONE;
688 osl::MutexGuard guard(jfw::FwkMutex());
689 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
690 return JFW_E_DIRECT_MODE;
691 const jfw::MergedSettings settings;
692 *ppCP = settings.getUserClassPath();
694 catch (const jfw::FrameworkException& e)
696 errcode = e.errorCode;
697 SAL_WARN( "jfw", e.message );
699 return errcode;
702 javaFrameworkError jfw_addJRELocation(OUString const & sLocation)
704 javaFrameworkError errcode = JFW_E_NONE;
707 osl::MutexGuard guard(jfw::FwkMutex());
708 if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
709 return JFW_E_DIRECT_MODE;
710 jfw::NodeJava node(jfw::NodeJava::USER);
711 node.load();
712 node.addJRELocation(sLocation);
713 node.write();
715 catch (const jfw::FrameworkException& e)
717 errcode = e.errorCode;
718 SAL_WARN( "jfw", e.message );
721 return errcode;
725 javaFrameworkError jfw_existJRE(const JavaInfo *pInfo, bool *exist)
727 javaPluginError plerr = jfw_plugin_existJRE(pInfo, exist);
729 javaFrameworkError ret = JFW_E_NONE;
730 switch (plerr)
732 case javaPluginError::NONE:
733 ret = JFW_E_NONE;
734 break;
735 case javaPluginError::Error:
736 ret = JFW_E_ERROR;
737 break;
738 default:
739 ret = JFW_E_ERROR;
741 return ret;
744 void jfw_lock()
746 jfw::FwkMutex().acquire();
749 void jfw_unlock()
751 jfw::FwkMutex().release();
754 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */