dlLibraryTable.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2017 OpenFOAM Foundation
9  Copyright (C) 2018-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "dlLibraryTable.H"
30 #include "OSspecific.H"
31 #include "IOstreams.H"
32 
33 // Could be constexpr in the header if required
34 #ifdef __APPLE__
35  #define EXT_SO "dylib"
36 #elif defined _WIN32
37  #define EXT_SO "dll"
38 #else
39  #define EXT_SO "so"
40 #endif
41 
42 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
43 
44 namespace Foam
45 {
46  defineTypeNameAndDebug(dlLibraryTable, 0);
47 }
48 
50 (
51  Foam::debug::optimisationSwitch("dlcloseOnTerminate", 0)
52 );
53 
54 
55 std::unique_ptr<Foam::dlLibraryTable> Foam::dlLibraryTable::global_(nullptr);
56 
57 
58 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
59 
61 {
62  word libName(libPath.stem());
63  libName.removeStart("lib"); // Remove leading 'lib' from name
64  return libName;
65 }
66 
67 
69 {
70  if (libName.empty())
71  {
72  return libName;
73  }
74 
75  // Add leading 'lib' and trailing '.so'
76  return "lib" + libName.ext(EXT_SO);
77 }
78 
79 
81 {
82  if (!global_)
83  {
84  global_.reset(new dlLibraryTable{});
85  }
86 
87  return *global_;
88 }
89 
90 
91 bool Foam::dlLibraryTable::functionHook
92 (
93  const bool load,
94  void* handle,
95  const std::string& funcName,
96  const bool verbose,
97  const std::string& context
98 )
99 {
100  if (!handle || funcName.empty())
101  {
102  return false;
103  }
104 
105  bool ok = false;
106 
107  void* symbol = Foam::dlSymFind(handle, funcName);
108 
109  if (symbol)
110  {
111  // Execute loader/unloader code
112  try
113  {
114  loaderType fun = reinterpret_cast<loaderType>(symbol);
115 
116  if (fun)
117  {
118  (*fun)(load);
119  ok = true;
120  }
121  }
122  catch (...)
123  {}
124  }
125 
126  if (verbose && !ok)
127  {
128  auto& err = WarningInFunction
129  << "Failed symbol lookup " << funcName.c_str() << nl;
130 
131  if (!context.empty())
132  {
133  err << "from " << context.c_str() << nl;
134  }
135  }
136 
137  return ok;
138 }
139 
140 
142 (
143  void* handle,
144  const std::string& funcName,
145  const bool verbose,
146  const std::string& context
147 )
148 {
149  return functionHook(true, handle, funcName, verbose, context);
150 }
151 
152 
154 (
155  void* handle,
156  const std::string& funcName,
157  const bool verbose,
158  const std::string& context
159 )
160 {
161  return functionHook(false, handle, funcName, verbose, context);
162 }
163 
164 
165 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
166 
167 void* Foam::dlLibraryTable::openLibrary
168 (
169  const fileName& libName,
170  bool verbose
171 )
172 {
173  if (libName.empty())
174  {
175  return nullptr;
176  }
177 
178  std::string msg;
179  void* ptr = Foam::dlOpen(fileName(libName).expand(), msg);
180 
182  << "Opened " << libName
183  << " resulting in handle " << Foam::name(ptr) << nl;
184 
185  if (!ptr)
186  {
187  // Even with details turned off, we want some feedback about failure
188  OSstream& os = (verbose > 0 ? WarningInFunction : InfoErr);
189  os << "Could not load " << libName << nl << msg.c_str() << endl;
190  }
191 
192  return ptr;
193 }
194 
195 
196 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
197 
199 (
200  const UList<fileName>& libNames,
201  bool verbose
202 )
203 {
204  dlLibraryTable::open(libNames, verbose);
205 }
206 
207 
209 (
210  std::initializer_list<fileName> libNames,
211  bool verbose
212 )
213 {
214  dlLibraryTable::open(libNames, verbose);
215 }
216 
217 
219 (
220  const word& libsEntry,
221  const dictionary& dict,
222  bool verbose
223 )
224 {
225  List<fileName> libNames;
226  dict.readIfPresent(libsEntry, libNames, keyType::LITERAL);
227  dlLibraryTable::open(libNames, verbose);
228 }
229 
230 
232 (
233  const dictionary& dict,
234  const word& libsEntry,
235  bool verbose
236 
237 )
238 :
239  dlLibraryTable(libsEntry, dict, verbose)
240 {}
241 
242 
243 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
244 
246 {
248  {
249  close();
250  }
251 }
252 
253 
254 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
255 
257 {
258  for (const void* ptr : libPtrs_)
259  {
260  if (ptr != nullptr)
261  {
262  return false;
263  }
264  }
265 
266  return true;
267 }
268 
269 
270 Foam::label Foam::dlLibraryTable::size() const noexcept
271 {
272  label nLoaded = 0;
273 
274  for (const void* ptr : libPtrs_)
275  {
276  if (ptr != nullptr)
277  {
278  ++nLoaded;
279  }
280  }
281 
282  return nLoaded;
283 }
284 
285 
287 {
288  libPtrs_.clear();
289  libNames_.clear();
290 }
291 
292 
294 {
295  List<fileName> list(libNames_.size());
296 
297  label nLoaded = 0;
298 
299  forAll(libNames_, i)
300  {
301  void* ptr = libPtrs_[i];
302  const fileName& libName = libNames_[i];
303 
304  if (ptr != nullptr && !libName.empty())
305  {
306  list[nLoaded] = libName;
307  ++nLoaded;
308  }
309  }
311  list.resize(nLoaded);
312 
313  return list;
314 }
315 
316 
317 void Foam::dlLibraryTable::close(bool verbose)
318 {
319  label nLoaded = 0;
320 
321  forAllReverse(libPtrs_, i)
322  {
323  void* ptr = libPtrs_[i];
324 
325  if (ptr == nullptr)
326  {
327  libNames_[i].clear();
328  continue;
329  }
330 
331  if (Foam::dlClose(ptr))
332  {
334  << "Closed [" << i << "] " << libNames_[i]
335  << " with handle " << Foam::name(ptr) << nl;
336 
337  libPtrs_[i] = nullptr;
338  libNames_[i].clear();
339  }
340  else
341  {
342  ++nLoaded; // Still loaded
343 
344  if (verbose)
345  {
347  << "Failed closing " << libNames_[i]
348  << " with handle " << Foam::name(ptr) << endl;
349  }
350  }
351  }
352 
353 
354  // Compact the lists
355  if (nLoaded && nLoaded != libPtrs_.size())
356  {
357  nLoaded = 0;
358 
359  forAll(libPtrs_, i)
360  {
361  if (libPtrs_[i] != nullptr)
362  {
363  if (nLoaded != i)
364  {
365  libPtrs_[nLoaded] = libPtrs_[i];
366  libNames_[nLoaded] = std::move(libNames_[i]);
367  }
368 
369  ++nLoaded;
370  }
371  }
372  }
373 
374  libPtrs_.resize(nLoaded);
375  libNames_.resize(nLoaded);
376 }
377 
378 
379 bool Foam::dlLibraryTable::append(const fileName& libName)
380 {
381  if (libName.empty() || libNames_.found(libName))
382  {
383  return false;
384  }
385 
386  libPtrs_.push_back(nullptr);
387  libNames_.push_back(libName);
388 
389  return true;
390 }
391 
392 
393 Foam::label Foam::dlLibraryTable::append(const UList<fileName>& libNames)
394 {
395  label nAdded = 0;
396 
397  for (const fileName& libName : libNames)
398  {
399  if (append(libName))
400  {
401  ++nAdded;
402  }
403  }
404 
405  return nAdded;
406 }
407 
408 
409 bool Foam::dlLibraryTable::open(bool verbose)
410 {
411  label nOpen = 0;
412  label nCand = 0; // Number of candidates (have libName but no pointer)
413 
414  forAll(libPtrs_, i)
415  {
416  void* ptr = libPtrs_[i];
417  const fileName& libName = libNames_[i];
418 
419  if (ptr == nullptr && !libName.empty())
420  {
421  ++nCand;
422 
423  ptr = openLibrary(libName, verbose);
424 
425  if (ptr)
426  {
427  ++nOpen;
428  libPtrs_[i] = ptr;
429  }
430  else
431  {
432  libNames_[i].clear(); // Avoid trying again
433  }
434  }
435  }
436 
437  return nOpen && nOpen == nCand;
438 }
439 
440 
442 (
443  const fileName& libName,
444  bool verbose
445 )
446 {
447  // Handles empty name silently
448  void* ptr = openLibrary(libName, verbose);
449 
450  if (ptr)
451  {
452  libPtrs_.push_back(ptr);
453  libNames_.push_back(libName);
454  }
455 
456  return ptr;
457 }
458 
459 
461 (
462  const UList<fileName>& libNames,
463  bool verbose
464 )
465 {
466  decltype(libNames.size()) nOpen = 0;
467 
468  for (const fileName& libName : libNames)
469  {
470  const label index = libNames_.find(libName);
471 
472  if (index >= 0 && libPtrs_[index] != nullptr)
473  {
474  // Already known and opened
475  ++nOpen;
476  }
477  else if (dlLibraryTable::open(libName, verbose))
478  {
479  ++nOpen;
480  }
481  }
482 
483  return nOpen && nOpen == libNames.size();
484 }
485 
486 
488 (
489  std::initializer_list<fileName> libNames,
490  bool verbose
491 )
492 {
493  decltype(libNames.size()) nOpen = 0;
494 
495  for (const fileName& libName : libNames)
496  {
497  const label index = libNames_.find(libName);
498 
499  if (index >= 0 && libPtrs_[index] != nullptr)
500  {
501  // Already known and opened
502  ++nOpen;
503  }
504  else if (dlLibraryTable::open(libName, verbose))
505  {
506  ++nOpen;
507  }
508  }
509 
510  return nOpen && nOpen == libNames.size();
511 }
512 
513 
515 (
516  const fileName& libName,
517  bool verbose
518 )
519 {
520  const label index = libNames_.rfind(libName);
521 
522  if (index < 0 || libName.empty())
523  {
524  return false;
525  }
526 
527  void* ptr = libPtrs_[index];
528 
529  if (ptr == nullptr)
530  {
531  libNames_[index].clear();
532  return false;
533  }
534 
536  << "Closing " << libName
537  << " with handle " << Foam::name(ptr) << nl;
538 
539  const bool ok = Foam::dlClose(ptr);
540 
541  libPtrs_[index] = nullptr;
542  libNames_[index].clear();
543 
544  if (ok)
545  {
546  // From man dlopen(3)
547  // ...
548  // a dynamically loaded shared object is not deallocated until
549  // dlclose() has been called on it as many times as dlopen()
550  // has succeeded on it.
551 
552  // Handle aliased library names
553  for (label idx = 0; (idx = libPtrs_.find(ptr, idx)) >= 0; ++idx)
554  {
555  (void) Foam::dlClose(ptr);
556  libPtrs_[idx] = nullptr;
557  libNames_[idx].clear();
558  }
559  }
560  else if (verbose)
561  {
563  << "Could not close " << libName << endl;
564  }
565 
566  return ok;
567 }
568 
569 
570 void* Foam::dlLibraryTable::findLibrary(const fileName& libName)
571 {
572  const label index = libNames_.rfind(libName);
573 
574  if (index < 0 || libName.empty())
575  {
576  return nullptr;
577  }
578 
579  return libPtrs_[index];
580 }
581 
582 
584 (
585  const word& libsEntry,
586  const dictionary& dict,
587  bool verbose
588 )
589 {
590  List<fileName> libNames;
591  return
592  (
593  dict.readIfPresent(libsEntry, libNames, keyType::LITERAL)
594  && dlLibraryTable::open(libNames, verbose)
595  );
596 }
597 
598 
600 (
601  const dictionary& dict,
602  const word& libsEntry
603 )
604 {
605  return dlLibraryTable::open(libsEntry, dict, true); // verbose = true
606 }
607 
608 
609 // * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
610 
611 Foam::Ostream& Foam::operator<<
612 (
613  Ostream& os,
614  const InfoProxy<dlLibraryTable>& iproxy
615 )
616 {
617  const auto& tbl = *iproxy;
618 
619  os << token::BEGIN_LIST << nl;
620 
621  // Lengths of pointers/names are guaranteed internally to be identical
622  forAll(tbl.pointers(), i)
623  {
624  const void* ptr = tbl.pointers()[i];
625  const fileName& libName = tbl.names()[i];
626 
627  // Also write out empty filenames
628  // (specified with '-lib' but did not load)
629 
630  os << Foam::name(ptr) << token::SPACE << libName << nl;
631  }
632 
633  os << token::END_LIST << nl;
634 
635  return os;
636 }
637 
638 
639 // ************************************************************************* //
void * dlSymFind(void *handle, const std::string &symbol, bool required=false)
Look for symbol in a dlopened library.
Definition: POSIX.C:1939
dictionary dict
A class for handling file names.
Definition: fileName.H:72
messageStream InfoErr
Information stream (stderr output on master, null elsewhere)
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:160
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
bool dlClose(void *handle)
Close a dlopened library using handle. Return true if successful.
Definition: POSIX.C:1927
void clear()
Clears the table, without attempting to close the libraries.
Begin list [isseparator].
Definition: token.H:161
List< fileName > loaded() const
Names of the libraries in use.
~dlLibraryTable()
Destructor. Closes all libraries loaded by the table.
static dlLibraryTable & libs()
Table of global libraries.
static std::string stem(const std::string &str)
Return the basename, without extension.
Definition: fileName.C:391
void close(bool verbose=true)
Close all libraries loaded by the table and remove the closed functions from the table.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
Useful combination of include files which define Sin, Sout and Serr and the use of IO streams general...
word ext() const
Return file name extension (part after last .)
Definition: wordI.H:171
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
static bool loadHook(void *handle, const std::string &funcName, const bool verbose=false, const std::string &context="")
Low-level interface to execute global "void funcName(true)" from the library, typically for additiona...
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
static word basename(const fileName &libPath)
Library basename without leading &#39;lib&#39; or trailing &#39;.so&#39;.
A class for handling words, derived from Foam::string.
Definition: word.H:63
#define DebugInFunction
Report an information message using Foam::Info.
Space [isspace].
Definition: token.H:131
static word fullname(word libName)
Library fullname, prefix with &#39;lib&#39;, suffix with &#39;.so&#39;.
End list [isseparator].
Definition: token.H:162
String literal.
Definition: keyType.H:82
dlLibraryTable()=default
Default construct.
A table of dynamically loaded libraries.
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: HashTable.H:105
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
const direction noexcept
Definition: Scalar.H:258
int optimisationSwitch(const char *name, const int deflt=0)
Lookup optimisation switch or add default value.
Definition: debug.C:234
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry if present, and assign to T val. FatalIOError if it is found and the number of tokens i...
OBJstream os(runTime.globalPath()/outputName)
defineTypeNameAndDebug(combustionModel, 0)
bool append(const fileName &libName)
Add to the list of names, but do not yet open.
bool empty() const noexcept
True if there are no libraries loaded by the table.
static bool unloadHook(void *handle, const std::string &funcName, const bool verbose=false, const std::string &context="")
Low-level interface to execute global "void funcName(false)" from the library, typically for unloadin...
rAUs append(new volScalarField(IOobject::groupName("rAU", phase1.name()), 1.0/(U1Eqn.A()+byDt(max(phase1.residualAlpha() - alpha1, scalar(0)) *rho1))))
#define WarningInFunction
Report a warning using Foam::Warning.
void * dlOpen(const fileName &libName, const bool check=true)
Open a shared library and return handle to library.
Definition: POSIX.C:1824
void * findLibrary(const fileName &libName)
Find the handle of the named library.
bool removeStart(const std::string &text)
Remove the given text from the start of the string.
Definition: string.C:207
#define EXT_SO
#define forAllReverse(list, i)
Reverse loop across all elements in list.
Definition: stdFoam.H:437
bool open(bool verbose=true)
Open named, but unopened libraries. These names will normally have been added with the append() metho...
static int dlcloseOnTerminate
Use dlclose() when clearing the dlLibraryTable.
label size() const noexcept
The number of libraries loaded by the table.
Namespace for OpenFOAM.
string expand(const std::string &s, const HashTable< string > &mapping, const char sigil='$')
Expand occurrences of variables according to the mapping and return the expanded string.
Definition: stringOps.C:705