CPN
Computational Process Networks
NodeLoader.cc
Go to the documentation of this file.
1 //=============================================================================
2 // Computational Process Networks class library
3 // Copyright (C) 1997-2006 Gregory E. Allen and The University of Texas
4 //
5 // This library is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Library General Public License as published
7 // by the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Library General Public License for more details.
14 //
15 // The GNU Public License is available in the file LICENSE, or you
16 // can write to the Free Software Foundation, Inc., 59 Temple Place -
17 // Suite 330, Boston, MA 02111-1307, USA, or you can find it on the
18 // World Wide Web at http://www.fsf.org.
19 //=============================================================================
23 #include "common_priv.h"
24 #include <cpn/bits/NodeLoader.h>
25 #include <cpn/NodeFactory.h>
27 #include <cpn/utils/PathUtils.h>
28 #include <cpn/utils/Directory.h>
30 #include <fstream>
31 #include <stdexcept>
32 #include <ltdl.h>
33 
34 void Loader(const std::string &ldfile, std::map<std::string, std::string> &data);
35 
37 
38 namespace CPN {
39 
41  PthreadMutexProtected al(lock);
42  if (lt_dlinit() > 0) {
43  throw std::runtime_error(lt_dlerror());
44  }
45  }
46 
48  PthreadMutexProtected al(lock);
49  factorymap.clear();
50  for (LibMap::iterator itr = libmap.begin(); itr != libmap.end(); ++itr) {
51  lt_dlclose(itr->second);
52  }
53  libmap.clear();
54  lt_dlexit();
55  }
56 
57  void NodeLoader::LoadSharedLib(const std::string &libname) {
58  PthreadMutexProtected al(lock);
59  InternalLoadLib(libname);
60  }
61 
62  void NodeLoader::LoadSharedLib(const std::vector<std::string> &list) {
63  for (std::vector<std::string>::const_iterator i = list.begin(), e = list.end(); i != e; ++i) {
64  LoadSharedLib(*i);
65  }
66  }
67 
68  void NodeLoader::LoadNodeList(const std::string &filename) {
69  Loader(filename, nodelibmap);
70  }
71 
72  void NodeLoader::LoadNodeList(const std::vector<std::string> &list) {
73  for (std::vector<std::string>::const_iterator i = list.begin(), e = list.end(); i != e; ++i) {
74  LoadNodeList(*i);
75  }
76  }
77 
78  void NodeLoader::SearchDirectory(const std::string &dirname) {
79  std::string ext = ".nodelist";
80  for (Directory dir(dirname); !dir.End(); dir.Next()) {
81  if (dir.IsRegularFile()) {
82  std::string fname = dir.BaseName();
83  if (fname[0] == '.') { continue; }
84  if (fname.size() < ext.size()
85  || fname.substr(fname.size() - ext.size(), ext.size()) != ext) {
86  continue;
87  }
88  LoadNodeList(dir.FullName());
89  }
90  }
91  }
92 
93  void NodeLoader::SearchDirectory(const std::vector<std::string> &dirnames) {
94  for (std::vector<std::string>::const_iterator i = dirnames.begin(), e = dirnames.end(); i != e; ++i) {
95  SearchDirectory(*i);
96  }
97  }
98 
99  NodeFactory *NodeLoader::GetFactory(const std::string &nodetype) {
100  PthreadMutexProtected al(lock);
101  FactoryMap::iterator entry = factorymap.find(nodetype);
102  if (entry == factorymap.end()) {
103  InternalLoad(nodetype);
104  entry = factorymap.find(nodetype);
105  ASSERT(entry != factorymap.end());
106  }
107  return entry->second.get();
108  }
109 
110  void NodeLoader::RegisterFactory(shared_ptr<NodeFactory> factory) {
111  PthreadMutexProtected al(lock);
112  std::string name = factory->GetName();
113  factorymap.insert(std::make_pair(name, factory));
114  }
115 
116  void NodeLoader::InternalLoadLib(const std::string &lib) {
117  if (libmap.find(lib) == libmap.end()) {
118  struct advise_t {
119  advise_t() {
120  if (lt_dladvise_init(&advise) > 0) {
121  throw std::runtime_error(lt_dlerror());
122  }
123  }
124  ~advise_t() {
125  lt_dladvise_destroy(&advise);
126  }
127  void global() {
128  if (lt_dladvise_global(&advise) > 0) {
129  throw std::runtime_error(lt_dlerror());
130  }
131  }
132  lt_dladvise advise;
133  } advise;
134  advise.global();
135  lt_dlhandle handle = lt_dlopenadvise(lib.c_str(), advise.advise);
136  if (!handle) {
137  throw std::runtime_error(lt_dlerror());
138  } else {
139  libmap.insert(std::make_pair(lib, handle));
140  }
141  }
142  }
143 
144  void NodeLoader::InternalLoad(const std::string &name) {
145  std::string sym = CPN_DEFAULT_INIT_SYMBOL_STR;
146  sym += name;
147  lt_dlhandle handle = 0;
148 
149  // Use a union to avoid having to cast
150  // from void pointer to function pointer explicitely.
151  // Doing so would violate strict aliasing.
152  union {
153  void *vptr;
154  CPNInitPrototype fn;
155  } init;
156 
157  handle = lt_dlopen(0);
158  if (!handle) {
159  throw std::runtime_error(lt_dlerror());
160  }
161  try {
162  lt_dlerror();
163  init.vptr = lt_dlsym(handle, sym.c_str());
164  const char *error = lt_dlerror();
165  if (error != 0) {
166  NodeLibMap::iterator itr = nodelibmap.find(sym);
167  if (itr == nodelibmap.end()) {
168  throw std::runtime_error("Unable to find node type " + name);
169  }
170  InternalLoadLib(itr->second);
171  lt_dlerror();
172  init.vptr = lt_dlsym(handle, sym.c_str());
173  error = lt_dlerror();
174  if (error != 0) {
175  throw std::runtime_error(error);
176  }
177  }
178  shared_ptr<NodeFactory> factory = init.fn();
179  factorymap.insert(std::make_pair(factory->GetName(), factory));
180  } catch (...) {
181  lt_dlclose(handle);
182  throw;
183  }
184  lt_dlclose(handle);
185  }
186 }
187 
188 using std::ifstream;
189 using std::vector;
190 using std::stringstream;
191 using std::string;
192 using std::map;
193 
194 
195 void ProcessLine(vector<string> &stack, const string &curdir,
196  map<string, string> &data, unsigned rep);
197 
198 void Loader(const string &ldfile, map<string, string> &data, unsigned rep) {
199  ASSERT(rep < 100, "Loader include overflow");
200  ifstream f;
201  f.open(ldfile.c_str());
202  ASSERT(f.is_open(), "Failed to open %s", ldfile.c_str());
203  string curdir = DirName(RealPath(ldfile));
204  vector<char> token_stack;
205  vector<string> stack;
206  bool in_comment = false;
207  bool escape = false;
208  bool in_single_quote = false;
209  bool in_double_quote = false;
210  while (f.good()) {
211  int c = f.get();
212  if (!f.good()) {
213  break;
214  }
215  if (in_comment) {
216  if (c == '\n' || c == '\r') {
217  in_comment = false;
218  } else {
219  continue;
220  }
221  }
222  if (in_single_quote) {
223  if (c == '\'') {
224  in_single_quote = false;
225  } else {
226  token_stack.push_back(c);
227  }
228  } else if (escape) {
229  token_stack.push_back(c);
230  escape = false;
231  } else if (in_double_quote) {
232  if (c == '"') {
233  in_double_quote = false;
234  } else {
235  token_stack.push_back(c);
236  }
237  } else {
238  switch (c) {
239  case '#':
240  in_comment = true;
241  break;
242  case '\'':
243  in_single_quote = true;
244  break;
245  case '"':
246  in_double_quote = true;
247  break;
248  case '\\':
249  escape = true;
250  break;
251  case ';':
252  if (!token_stack.empty()) {
253  stack.push_back(string(&token_stack[0], token_stack.size()));
254  token_stack.clear();
255  }
256  ProcessLine(stack, curdir, data, rep);
257  break;
258  case '\n':
259  case '\r':
260  // end of line
261  case ' ':
262  case '\t':
263  case '\v':
264  case '\f':
265  // white space
266  if (!token_stack.empty()) {
267  // end the current token, otherwise nothing
268  stack.push_back(string(&token_stack[0], token_stack.size()));
269  token_stack.clear();
270  }
271  break;
272  default:
273  token_stack.push_back(c);
274  break;
275  }
276  }
277  }
278  ProcessLine(stack, curdir, data, rep);
279 }
280 
281 void ProcessLine(vector<string> &stack, const string &curdir,
282  map<string, string> &data, unsigned rep) {
283  if (stack.empty()) return;
284  vector<string>::iterator itr = stack.begin();
285  if (*itr == "lib") {
286  ++itr;
287  if (itr != stack.end()) {
288  string libname = *itr;
289  if (!IsAbsPath(libname)) {
290  libname = RealPath(PathConcat(curdir, libname));
291  }
292  if (!libname.empty()) {
293  ++itr;
294  while (itr != stack.end()) {
295  data[*itr] = libname;
296  ++itr;
297  }
298  }
299  }
300  } else if (*itr == "include") {
301  ++itr;
302  while (itr != stack.end()) {
303  string inc = *itr;
304  if (!IsAbsPath(inc)) {
305  inc = RealPath(PathConcat(curdir, inc));
306  }
307  Loader(inc, data, rep + 1);
308  ++itr;
309  }
310  }
311  stack.clear();
312 }
313 
314 void Loader(const string &ldfile, map<string, string> &data) {
315  Loader(ldfile, data, 0);
316 }
317 
shared_ptr< NodeFactory >(* CPNInitPrototype)(void)
This is the prototype of the function that is called by the dynamic library loading facility...
Definition: NodeLoader.h:39
bool End() const
Definition: Directory.h:48
void LoadSharedLib(const std::string &libname)
Definition: NodeLoader.cc:57
#define CPN_DEFAULT_INIT_SYMBOL_STR
Definition: NodeLoader.h:32
void SearchDirectory(const std::string &dirname)
Definition: NodeLoader.cc:78
void RegisterFactory(shared_ptr< NodeFactory > factory)
Definition: NodeLoader.cc:110
NodeFactory * GetFactory(const std::string &nodename)
Definition: NodeLoader.cc:99
void Loader(const std::string &ldfile, std::map< std::string, std::string > &data)
Definition: NodeLoader.cc:314
FactoryMap factorymap
Definition: NodeLoader.h:91
void InternalLoadLib(const std::string &lib)
Definition: NodeLoader.cc:116
void InternalLoad(const std::string &sym)
Definition: NodeLoader.cc:144
void ProcessLine(vector< string > &stack, const string &curdir, map< string, string > &data, unsigned rep)
Definition: NodeLoader.cc:281
void LoadNodeList(const std::string &filename)
Definition: NodeLoader.cc:68
std::string RealPath(const std::string &path)
Definition: PathUtils.cc:29
NodeLibMap nodelibmap
Definition: NodeLoader.h:93
std::string DirName(const std::string &path)
Definition: PathUtils.cc:39
static PthreadMutex lock
Definition: NodeLoader.cc:36
std::string PathConcat(const std::string &dir, const std::string &file)
Definition: PathUtils.cc:85
bool IsAbsPath(const std::string &path)
Definition: PathUtils.cc:89
The node factory provides a method for the kernel to create arbitrary user defined Nodes...
Definition: NodeFactory.h:37
#define ASSERT(exp,...)
Definition of the NodeFactory.