Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TMapFile.cxx
Go to the documentation of this file.
1 // @(#)root/io:$Id$
2 // Author: Fons Rademakers 08/07/97
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 #ifdef WIN32
12 #pragma optimize("",off)
13 #endif
14 
15 /**
16 \class TMapFile
17 \ingroup IO
18 
19 This class implements a shared memory region mapped to a file.
20 Objects can be placed into this shared memory area using the Add()
21 member function. To actually place a copy of the object is shared
22 memory call Update() also whenever the mapped object(s) change(s)
23 call Update() to put a fresh copy in the shared memory. This extra
24 step is necessary since it is not possible to share objects with
25 virtual pointers between processes (the vtbl ptr points to the
26 originators unique address space and can not be used by the
27 consumer process(es)). Consumer processes can map the memory region
28 from this file and access the objects stored in it via the Get()
29 method (which returns a copy of the object stored in the shared
30 memory with correct vtbl ptr set). Only objects of classes with a
31 Streamer() member function defined can be shared.
32 
33 I know the current implementation is not ideal (you need to copy to
34 and from the shared memory file) but the main problem is with the
35 class' virtual_table pointer. This pointer points to a table unique
36 for every process. Therefore, different options are:
37  -# One could allocate an object directly in shared memory in the
38  producer, but the consumer still has to copy the object from
39  shared memory into a local object which has the correct vtbl
40  pointer for that process (copy ctor's can be used for creating
41  the local copy).
42  -# Another possibility is to only allow objects without virtual
43  functions in shared memory (like simple C structs), or to
44  forbid (how?) the consumer from calling any virtual functions
45  of the objects in shared memory.
46  -# A last option is to copy the object internals to shared memory
47  and copy them again from there. This is what is done in the
48  TMapFile (using the object Streamer() to make a deep copy).
49 
50 Option 1) saves one copy, but requires solid copy ctor's (along the
51 full inheritance chain) to rebuild the object in the consumer. Most
52 classes don't provide these copy ctor's, especially not when objects
53 contain collections, etc. 2) is too limiting or dangerous (calling
54 accidentally a virtual function will segv). So since we have a
55 robust Streamer mechanism I opted for 3).
56 **/
57 
58 
59 #ifdef WIN32
60 # include <windows.h>
61 # include <process.h>
62 # ifdef GetObject
63 # undef GetObject
64 # endif
65 # define HAVE_SEMOP
66 
67 # ifdef CreateSemaphore
68 # undef CreateSemaphore
69 # endif
70 
71 # ifdef AcquireSemaphore
72 # undef AcquireSemaphore;
73 # endif
74 
75 # ifdef ReleaseSemaphore
76 # undef ReleaseSemaphore
77 # endif
78 
79 # ifdef DeleteSemaphore
80 # undef DeleteSemaphore
81 # endif
82 
83 #else
84 # define INVALID_HANDLE_VALUE -1
85 #endif
86 
87 #include <fcntl.h>
88 #include <errno.h>
89 
90 #include "TMapFile.h"
91 #include "TKeyMapFile.h"
92 #include "TDirectoryFile.h"
93 #include "TBrowser.h"
94 #include "TStorage.h"
95 #include "TString.h"
96 #include "TSystem.h"
97 #include "TClass.h"
98 #include "TBufferFile.h"
99 #include "TVirtualMutex.h"
100 #include "mmprivate.h"
101 
102 #include <cmath>
103 
104 #if defined(R__UNIX) && !defined(R__MACOSX) && !defined(R__WINGCC)
105 #define HAVE_SEMOP
106 #include <sys/types.h>
107 #include <sys/ipc.h>
108 #include <sys/sem.h>
109 #if defined(R__HPUX) || \
110  defined (R__SOLARIS) || defined(R__AIX) || defined(R__HIUX) || \
111  __GLIBC_MINOR__ > 0
112 union semun {
113  int val; // value for SETVAL
114  struct semid_ds *buf; // buffer for IPC_STAT & IPC_SET
115  ushort *array; // array for GETALL & SETALL
116 };
117 #endif
118 #if defined(R__LINUX) || defined(R__LYNXOS) || defined(R__HURD)
119 # define SEM_A 0200 // alter permission
120 # define SEM_R 0400 // read permission
121 #endif
122 #endif
123 
124 
125 Long_t TMapFile::fgMapAddress = 0;
126 void *TMapFile::fgMmallocDesc = 0;
127 
128 //void *ROOT::Internal::gMmallocDesc = 0; //is initialized in TStorage.cxx
129 
130 
131 namespace {
132 ////////////////////////////////////////////////////////////////////////////////
133 /// Delete memory and return true if memory belongs to a TMapFile.
134  static bool FreeIfTMapFile(void* ptr) {
135  if (TMapFile *mf = TMapFile::WhichMapFile(ptr)) {
136  if (mf->IsWritable())
137  ::mfree(mf->GetMmallocDesc(), ptr);
138  return true;
139  }
140  return false;
141  }
142 }
143 
144 
145 ////////////////////////////////////////////////////////////////////////////////
146 /// Set ROOT::Internal::gFreeIfTMapFile on library load.
147 
148 struct SetFreeIfTMapFile_t {
149  SetFreeIfTMapFile_t() {
150  ROOT::Internal::gFreeIfTMapFile = FreeIfTMapFile;
151  }
152  ~SetFreeIfTMapFile_t() {
153  ROOT::Internal::gFreeIfTMapFile = nullptr;
154  }
155 } gSetFreeIfTMapFile;
156 
157 
158 ////////////////////////////////////////////////////////////////////////////////
159 //// Constructor.
160 
161 TMapRec::TMapRec(const char *name, const TObject *obj, Int_t size, void *buf)
162 {
163  fName = StrDup(name);
164  fClassName = 0;
165  fObject = (TObject*)obj;
166  fBuffer = buf;
167  fBufSize = size;
168  fNext = 0;
169 }
170 
171 ////////////////////////////////////////////////////////////////////////////////
172 /// Destructor.
173 
174 TMapRec::~TMapRec()
175 {
176  delete [] fName;
177  delete [] fClassName;
178 }
179 
180 ////////////////////////////////////////////////////////////////////////////////
181 /// This method returns a pointer to the original object.
182 
183 /// NOTE: this pointer is only valid in the process that produces the shared
184 /// memory file. In a consumer process this pointer is illegal! Be careful.
185 
186 TObject *TMapRec::GetObject() const
187 {
188  return fObject;
189 }
190 
191 
192 
193 
194 ClassImp(TMapFile);
195 
196 ////////////////////////////////////////////////////////////////////////////////
197 /// Default ctor. Does not much except setting some basic values.
198 
199 TMapFile::TMapFile()
200 {
201  fFd = -1;
202  fVersion = 0;
203  fName = 0;
204  fTitle = 0;
205  fOption = 0;
206  fMmallocDesc = 0;
207  fBaseAddr = 0;
208  fSize = 0;
209  fFirst = 0;
210  fLast = 0;
211  fOffset = 0;
212  fDirectory = 0;
213  fBrowseList = 0;
214  fWritable = kFALSE;
215  fSemaphore = -1;
216  fhSemaphore = 0;
217  fGetting = 0;
218  fWritten = 0;
219  fSumBuffer = 0;
220  fSum2Buffer = 0;
221 }
222 
223 ////////////////////////////////////////////////////////////////////////////////
224 /// Create a memory mapped file.
225 ///
226 /// This opens a file (to which the
227 /// memory will be mapped) and attaches a memory region to it.
228 /// Option can be either: "NEW", "CREATE", "RECREATE", "UPDATE" or
229 /// "READ" (see TFile). The default open mode is "READ". The size
230 /// argument specifies the maximum size of shared memory file in bytes.
231 /// This protected ctor is called via the static Create() method.
232 
233 TMapFile::TMapFile(const char *name, const char *title, Option_t *option,
234  Int_t size, TMapFile *&newMapFile)
235 {
236 #ifndef WIN32
237  fFd = -1;
238  fSemaphore = -1;
239  fhSemaphore = 0;
240 #else
241  fFd = (Int_t) INVALID_HANDLE_VALUE;
242  fSemaphore = (Int_t) INVALID_HANDLE_VALUE;
243 #endif
244  fMmallocDesc = 0;
245  fSize = size;
246  fFirst = 0;
247  fOffset = 0;
248  fVersion = gROOT->GetVersionInt();
249  fTitle = StrDup(title);
250  fOption = StrDup(option);
251  fDirectory = 0;
252  fBrowseList = 0;
253  fGetting = 0;
254  fWritten = 0;
255  fSumBuffer = 0;
256  fSum2Buffer = 0;
257 
258  char *cleanup = 0;
259  Bool_t create = kFALSE;
260  Bool_t recreate, update, read;
261 
262  {
263  TString opt = option;
264 
265  if (!opt.CompareTo("NEW", TString::kIgnoreCase) ||
266  !opt.CompareTo("CREATE", TString::kIgnoreCase))
267  create = kTRUE;
268  recreate = opt.CompareTo("RECREATE", TString::kIgnoreCase)
269  ? kFALSE : kTRUE;
270  update = opt.CompareTo("UPDATE", TString::kIgnoreCase)
271  ? kFALSE : kTRUE;
272  read = opt.CompareTo("READ", TString::kIgnoreCase)
273  ? kFALSE : kTRUE;
274  if (!create && !recreate && !update && !read) {
275  read = kTRUE;
276  delete [] fOption;
277  fOption = StrDup("READ");
278  }
279  }
280 
281  const char *fname;
282  if ((fname = gSystem->ExpandPathName(name))) {
283  fName = StrDup(fname);
284  delete [] (char*)fname;
285  fname = fName;
286  } else {
287  Error("TMapFile", "error expanding path %s", name);
288  goto zombie;
289  }
290 
291  if (recreate) {
292  if (!gSystem->AccessPathName(fname, kFileExists))
293  gSystem->Unlink(fname);
294  recreate = kFALSE;
295  create = kTRUE;
296  delete [] fOption;
297  fOption = StrDup("CREATE");
298  }
299  if (create && !gSystem->AccessPathName(fname, kFileExists)) {
300  Error("TMapFile", "file %s already exists", fname);
301  goto zombie;
302  }
303  if (update) {
304  if (gSystem->AccessPathName(fname, kFileExists)) {
305  update = kFALSE;
306  create = kTRUE;
307  }
308  if (update && gSystem->AccessPathName(fname, kWritePermission)) {
309  Error("TMapFile", "no write permission, could not open file %s", fname);
310  goto zombie;
311  }
312  }
313  if (read) {
314  if (gSystem->AccessPathName(fname, kFileExists)) {
315  Error("TMapFile", "file %s does not exist", fname);
316  goto zombie;
317  }
318  if (gSystem->AccessPathName(fname, kReadPermission)) {
319  Error("TMapFile", "no read permission, could not open file %s", fname);
320  goto zombie;
321  }
322  }
323 
324  // Open file to which memory will be mapped
325  if (create || update) {
326 #ifndef WIN32
327  fFd = open(fname, O_RDWR | O_CREAT, 0644);
328 #else
329  fFd = (Int_t) CreateFile(fname, // pointer to name of the file
330  GENERIC_WRITE | GENERIC_READ, // access (read-write) mode
331  FILE_SHARE_WRITE | FILE_SHARE_READ, // share mode
332  NULL, // pointer to security attributes
333  OPEN_ALWAYS, // how to create
334  FILE_ATTRIBUTE_TEMPORARY, // file attributes
335  (HANDLE) NULL); // handle to file with attributes to copy
336 #endif
337  if (fFd == (Int_t)INVALID_HANDLE_VALUE) {
338  SysError("TMapFile", "file %s can not be opened", fname);
339  goto zombie;
340  }
341  fWritable = kTRUE;
342  } else {
343 #ifndef WIN32
344  fFd = open(fname, O_RDONLY);
345 #else
346  fFd = (Int_t) CreateFile(fname, // pointer to name of the file
347  GENERIC_READ, // access (read-write) mode
348  FILE_SHARE_WRITE | FILE_SHARE_READ, // share mode
349  NULL, // pointer to security attributes
350  OPEN_EXISTING, // how to create
351  FILE_ATTRIBUTE_TEMPORARY, // file attributes
352  (HANDLE) NULL); // handle to file with attributes to copy
353 #endif
354  if (fFd == (Int_t)INVALID_HANDLE_VALUE) {
355  SysError("TMapFile", "file %s can not be opened for reading", fname);
356  goto zombie;
357  }
358  fWritable = kFALSE;
359  }
360 
361  // Attach memory region to file.
362  void *mapto;
363  TMapFile *mapfil;
364 
365  if (((mapto = MapToAddress()) == (void *)-1) ||
366 #ifndef WIN32
367  ((fMmallocDesc = mmalloc_attach(fFd, mapto, fSize)) == 0)) {
368 #else
369  ((fMmallocDesc = mmalloc_attach((HANDLE) fFd, mapto, fSize)) == 0)) {
370 #endif
371 
372  if (mapto == (void *)-1) {
373  Error("TMapFile", "no memory mapped file capability available\n"
374  "Use rootn.exe or link application against \"-lNew\"");
375  } else {
376  if (fMmallocDesc == 0 && fWritable)
377  Error("TMapFile", "mapped file not in mmalloc format or\n"
378  "already open in RW mode by another process");
379  if (fMmallocDesc == 0 && !fWritable)
380  Error("TMapFile", "mapped file not in mmalloc format");
381  }
382 #ifndef WIN32
383  close(fFd);
384 #else
385  CloseHandle((HANDLE) fFd);
386 #endif
387  fFd = -1;
388  if (create)
389  gSystem->Unlink(fname);
390  goto zombie;
391 
392  } else if ((mapfil = (TMapFile *) mmalloc_getkey(fMmallocDesc, 0)) != 0) {
393 
394  // File contains mmalloc heap. If we are in write mode and mapped
395  // file already connected in write mode switch to read-only mode.
396  // Check if ROOT versions are compatible.
397  // If so update mapped version of TMapFile to reflect current
398  // situation (only if not opened in READ mode).
399  if (mapfil->fVersion != fVersion) {
400  Error("TMapFile", "map file %s (%d) incompatible with current ROOT version (%d)",
401  fname, mapfil->fVersion, fVersion);
402  mmalloc_detach(fMmallocDesc);
403 #ifndef WIN32
404  close(fFd);
405 #else
406  CloseHandle((HANDLE) fFd);
407 #endif
408  fFd = -1;
409  fMmallocDesc = 0;
410  goto zombie;
411  }
412 
413  if (mapfil->fWritable && fWritable) {
414  Warning("TMapFile", "map file already open in write mode, opening in read-only mode");
415  fWritable = kFALSE;
416  }
417 
418  fBaseAddr = mapfil->fBaseAddr;
419  fSize = mapfil->fSize;
420 
421  if (fWritable) {
422  // create new TMapFile object in mapped heap to get correct vtbl ptr
423  CreateSemaphore();
424  ROOT::Internal::gMmallocDesc = fMmallocDesc;
425  TMapFile *mf = new TMapFile(*mapfil);
426  mf->fFd = fFd;
427  mf->fWritable = kTRUE;
428  cleanup = mf->fOption;
429  mf->fOption = StrDup(fOption);
430  mf->fSemaphore = fSemaphore;
431 #ifdef WIN32
432  mf->CreateSemaphore(fSemaphore);
433 #endif
434  mmalloc_setkey(fMmallocDesc, 0, mf);
435  ROOT::Internal::gMmallocDesc = 0;
436  mapfil = mf;
437  } else {
438  ROOT::Internal::gMmallocDesc = 0; // make sure we are in sbrk heap
439  fOffset = ((struct mdesc *) fMmallocDesc)->offset;
440  TMapFile *mf = new TMapFile(*mapfil, fOffset);
441  delete [] mf->fOption;
442  mf->fFd = fFd;
443  mf->fOption = StrDup("READ");
444  mf->fMmallocDesc = fMmallocDesc;
445  mf->fWritable = kFALSE;
446  mapfil = mf;
447  }
448 
449  // store shadow mapfile (it contains the real fFd in case map
450  // is not writable)
451  fVersion = -1; // make this the shadow map file
452  R__LOCKGUARD(gROOTMutex);
453  gROOT->GetListOfMappedFiles()->AddLast(this);
454 
455  } else {
456 
457  // New file. If the file is writable create a new copy of the
458  // TMapFile which will now be allocated on the memory mapped heap.
459  if (!fWritable) {
460  Error("TMapFile", "map file is not writable");
461  mmalloc_detach(fMmallocDesc);
462 #ifndef WIN32
463  close(fFd);
464 #else
465  CloseHandle((HANDLE) fFd);
466 #endif
467  fFd = -1;
468  fMmallocDesc = 0;
469  goto zombie;
470  }
471 
472  fBaseAddr = (ULong_t)((struct mdesc *) fMmallocDesc)->base;
473 
474  CreateSemaphore();
475 
476  ROOT::Internal::gMmallocDesc = fMmallocDesc;
477 
478  mapfil = new TMapFile(*this);
479  mmalloc_setkey(fMmallocDesc, 0, mapfil);
480 
481  ROOT::Internal::gMmallocDesc = 0;
482 
483  // store shadow mapfile
484  fVersion = -1; // make this the shadow map file
485  R__LOCKGUARD(gROOTMutex);
486  gROOT->GetListOfMappedFiles()->AddLast(this);
487 
488  }
489 
490  mapfil->InitDirectory();
491  {
492  R__LOCKGUARD(gROOTMutex);
493  gROOT->GetListOfMappedFiles()->AddFirst(mapfil);
494  }
495 
496  if (cleanup) delete [] cleanup;
497 
498  newMapFile = mapfil;
499 
500  return;
501 
502 zombie:
503  // error in file opening occured, make this object a zombie
504  MakeZombie();
505  newMapFile = this;
506  ROOT::Internal::gMmallocDesc = 0;
507 }
508 
509 ////////////////////////////////////////////////////////////////////////////////
510 /// Private copy ctor.
511 ///
512 /// Used by the the ctor to create a new version
513 /// of TMapFile in the memory mapped heap. It's main purpose is to
514 /// correctly create the string data members.
515 
516 TMapFile::TMapFile(const TMapFile &f, Long_t offset) : TObject(f)
517 {
518  fFd = f.fFd;
519  fVersion = f.fVersion;
520  fName = StrDup((char *)((Long_t)f.fName + offset));
521  fTitle = StrDup((char *)((Long_t)f.fTitle + offset));
522  fOption = StrDup((char *)((Long_t)f.fOption + offset));
523  fMmallocDesc = f.fMmallocDesc;
524  fBaseAddr = f.fBaseAddr;
525  fSize = f.fSize;
526  fFirst = f.fFirst;
527  fLast = f.fLast;
528  fWritable = f.fWritable;
529  fSemaphore = f.fSemaphore;
530  fOffset = offset;
531  fDirectory = 0;
532  fBrowseList = 0;
533  fGetting = 0;
534  fWritten = f.fWritten;
535  fSumBuffer = f.fSumBuffer;
536  fSum2Buffer = f.fSum2Buffer;
537 #ifdef WIN32
538  CreateSemaphore(fSemaphore);
539 #else
540  fhSemaphore = f.fhSemaphore;
541 #endif
542 }
543 
544 ////////////////////////////////////////////////////////////////////////////////
545 /// TMapFiles may not be deleted, since we want to keep the complete
546 /// TMapFile object in the mapped file for later re-use. To enforce this
547 /// the delete operator has been made private. Use Close() to properly
548 /// terminate a TMapFile (also done via the TROOT dtor).
549 
550 TMapFile::~TMapFile()
551 {
552  if (fDirectory == gDirectory) gDirectory = gROOT;
553  delete fDirectory; fDirectory = 0;
554  if (fBrowseList) fBrowseList->Delete();
555  delete fBrowseList; fBrowseList = 0;
556 
557  // if shadow map file we are done here
558  if (fVersion == -1)
559  return;
560 
561  // Writable mapfile is allocated in mapped memory. This object should
562  // not be deleted by ::operator delete(), because it is needed if we
563  // want to connect later to the file again.
564  if (fWritable)
565  TObject::SetDtorOnly(this);
566 
567  Close("dtor");
568 
569  fgMmallocDesc = fMmallocDesc;
570 }
571 
572 ////////////////////////////////////////////////////////////////////////////////
573 /// Create the directory associated to this mapfile
574 
575 void TMapFile::InitDirectory()
576 {
577  gDirectory = 0;
578  fDirectory = new TDirectoryFile();
579  fDirectory->SetName(GetName());
580  fDirectory->SetTitle(GetTitle());
581  fDirectory->Build();
582  fDirectory->SetMother(this);
583  gDirectory = fDirectory;
584 }
585 
586 ////////////////////////////////////////////////////////////////////////////////
587 /// Add an object to the list of objects to be stored in shared memory.
588 /// To place the object actually into shared memory call Update().
589 
590 void TMapFile::Add(const TObject *obj, const char *name)
591 {
592  if (!fWritable || !fMmallocDesc) return;
593 
594  Bool_t lock = fGetting != obj ? kTRUE : kFALSE;
595 
596  if (lock)
597  AcquireSemaphore();
598 
599  ROOT::Internal::gMmallocDesc = fMmallocDesc;
600 
601  const char *n;
602  if (name && *name)
603  n = name;
604  else
605  n = obj->GetName();
606 
607  if (Remove(n, kFALSE)) {
608  //Warning("Add", "replaced object with same name %s", n);
609  }
610 
611  TMapRec *mr = new TMapRec(n, obj, 0, 0);
612  if (!fFirst) {
613  fFirst = mr;
614  fLast = mr;
615  } else {
616  fLast->fNext = mr;
617  fLast = mr;
618  }
619 
620  ROOT::Internal::gMmallocDesc = 0;
621 
622  if (lock)
623  ReleaseSemaphore();
624 }
625 
626 ////////////////////////////////////////////////////////////////////////////////
627 /// Update an object (or all objects, if obj == 0) in shared memory.
628 
629 void TMapFile::Update(TObject *obj)
630 {
631  if (!fWritable || !fMmallocDesc) return;
632 
633  AcquireSemaphore();
634 
635  ROOT::Internal::gMmallocDesc = fMmallocDesc;
636 
637  Bool_t all = (obj == 0) ? kTRUE : kFALSE;
638 
639  TMapRec *mr = fFirst;
640  while (mr) {
641  if (all || mr->fObject == obj) {
642  TBufferFile *b;
643  if (!mr->fBufSize) {
644  b = new TBufferFile(TBuffer::kWrite, GetBestBuffer());
645  mr->fClassName = StrDup(mr->fObject->ClassName());
646  } else
647  b = new TBufferFile(TBuffer::kWrite, mr->fBufSize, mr->fBuffer);
648  b->MapObject(mr->fObject); //register obj in map to handle self reference
649  mr->fObject->Streamer(*b);
650  mr->fBufSize = b->BufferSize();
651  mr->fBuffer = b->Buffer();
652  SumBuffer(b->Length());
653  b->DetachBuffer();
654  delete b;
655  }
656  mr = mr->fNext;
657  }
658 
659  ROOT::Internal::gMmallocDesc = 0;
660 
661  ReleaseSemaphore();
662 }
663 
664 ////////////////////////////////////////////////////////////////////////////////
665 /// Remove object from shared memory.
666 ///
667 /// Returns pointer to removed object if successful, 0 otherwise.
668 
669 TObject *TMapFile::Remove(TObject *obj, Bool_t lock)
670 {
671  if (!fWritable || !fMmallocDesc) return 0;
672 
673  if (lock)
674  AcquireSemaphore();
675 
676  TObject *retObj = 0;
677  TMapRec *prev = 0, *mr = fFirst;
678  while (mr) {
679  if (mr->fObject == obj) {
680  if (mr == fFirst) {
681  fFirst = mr->fNext;
682  if (mr == fLast)
683  fLast = 0;
684  } else {
685  prev->fNext = mr->fNext;
686  if (mr == fLast)
687  fLast = prev;
688  }
689  retObj = obj;
690  delete mr;
691  break;
692  }
693  prev = mr;
694  mr = mr->fNext;
695  }
696 
697  if (lock)
698  ReleaseSemaphore();
699 
700  return retObj;
701 }
702 
703 ////////////////////////////////////////////////////////////////////////////////
704 /// Remove object by name from shared memory.
705 ///
706 /// Returns pointer to removed object if successful, 0 otherwise.
707 
708 TObject *TMapFile::Remove(const char *name, Bool_t lock)
709 {
710  if (!fWritable || !fMmallocDesc) return 0;
711 
712  if (lock)
713  AcquireSemaphore();
714 
715  TObject *retObj = 0;
716  TMapRec *prev = 0, *mr = fFirst;
717  while (mr) {
718  if (!strcmp(mr->fName, name)) {
719  if (mr == fFirst) {
720  fFirst = mr->fNext;
721  if (mr == fLast)
722  fLast = 0;
723  } else {
724  prev->fNext = mr->fNext;
725  if (mr == fLast)
726  fLast = prev;
727  }
728  retObj = mr->fObject;
729  delete mr;
730  break;
731  }
732  prev = mr;
733  mr = mr->fNext;
734  }
735 
736  if (lock)
737  ReleaseSemaphore();
738 
739  return retObj;
740 }
741 
742 ////////////////////////////////////////////////////////////////////////////////
743 /// Remove all objects from shared memory.
744 
745 void TMapFile::RemoveAll()
746 {
747  if (!fWritable || !fMmallocDesc) return;
748 
749  AcquireSemaphore();
750 
751  TMapRec *mr = fFirst;
752  while (mr) {
753  TMapRec *t = mr;
754  mr = mr->fNext;
755  delete t;
756  }
757  fFirst = fLast = 0;
758 
759  ReleaseSemaphore();
760 }
761 
762 ////////////////////////////////////////////////////////////////////////////////
763 /// Return pointer to object retrieved from shared memory.
764 ///
765 /// The object must
766 /// be deleted after use. If delObj is a pointer to a previously allocated
767 /// object it will be deleted. Returns 0 in case object with the given
768 /// name does not exist.
769 
770 TObject *TMapFile::Get(const char *name, TObject *delObj)
771 {
772  if (!fMmallocDesc) return 0;
773 
774  AcquireSemaphore();
775 
776  delete delObj;
777 
778  TObject *obj = 0;
779  TMapRec *mr = GetFirst();
780  while (OrgAddress(mr)) {
781  if (!strcmp(mr->GetName(fOffset), name)) {
782  if (!mr->fBufSize) goto release;
783  TClass *cl = TClass::GetClass(mr->GetClassName(fOffset));
784  if (!cl) {
785  Error("Get", "unknown class %s", mr->GetClassName(fOffset));
786  goto release;
787  }
788 
789  obj = (TObject *)cl->New();
790  if (!obj) {
791  Error("Get", "cannot create new object of class %s", mr->GetClassName(fOffset));
792  goto release;
793  }
794 
795  fGetting = obj;
796  TBufferFile *b = new TBufferFile(TBuffer::kRead, mr->fBufSize, mr->GetBuffer(fOffset));
797  b->MapObject(obj); //register obj in map to handle self reference
798  obj->Streamer(*b);
799  b->DetachBuffer();
800  delete b;
801  fGetting = 0;
802  goto release;
803  }
804  mr = mr->GetNext(fOffset);
805  }
806 
807 release:
808  ReleaseSemaphore();
809 
810  return obj;
811 }
812 
813 ////////////////////////////////////////////////////////////////////////////////
814 /// Create semaphore used for synchronizing access to shared memory.
815 
816 #ifndef WIN32
817 void TMapFile::CreateSemaphore(int)
818 #else
819 void TMapFile::CreateSemaphore(int pid)
820 #endif
821 {
822 #ifdef HAVE_SEMOP
823 #ifndef WIN32
824  // create semaphore to synchronize access (should use read/write lock)
825  fSemaphore = semget(IPC_PRIVATE, 1, SEM_R|SEM_A|(SEM_R>>3)|(SEM_A>>3)|
826  (SEM_R>>6)|(SEM_A>>6));
827 
828  // set semaphore to 1
829  if (fSemaphore != -1) {
830  union semun set;
831  set.val = 1;
832  semctl(fSemaphore, 0, SETVAL, set);
833  }
834 #else
835  char buffer[] ="ROOT_Semaphore_xxxxxxxx";
836  int lbuf = strlen(buffer);
837  if (!pid) fSemaphore = getpid();
838  fhSemaphore = (ULong_t)CreateMutex(NULL,FALSE,itoa(fSemaphore,&buffer[lbuf-8],16));
839  if (fhSemaphore == 0) fSemaphore = (Int_t)INVALID_HANDLE_VALUE;
840 #endif
841 #endif
842 }
843 
844 ////////////////////////////////////////////////////////////////////////////////
845 /// Delete the semaphore.
846 
847 void TMapFile::DeleteSemaphore()
848 {
849 #ifdef HAVE_SEMOP
850  // remove semaphore
851 #ifndef WIN32
852  if (fSemaphore != -1) {
853  int semid = fSemaphore;
854  fSemaphore = -1;
855  union semun set;
856  set.val = 0;
857  semctl(semid, 0, IPC_RMID, set);
858  }
859 #else
860  if (fSemaphore != (Int_t)INVALID_HANDLE_VALUE) {
861  CloseHandle((HANDLE)fhSemaphore);
862  fhSemaphore = 0;
863  fSemaphore = (Int_t)INVALID_HANDLE_VALUE;
864  }
865 #endif
866 #endif
867 }
868 
869 ////////////////////////////////////////////////////////////////////////////////
870 /// Acquire semaphore. Returns 0 if OK, -1 on error.
871 
872 Int_t TMapFile::AcquireSemaphore()
873 {
874 #ifdef HAVE_SEMOP
875 #ifndef WIN32
876  if (fSemaphore != -1) {
877  struct sembuf buf = { 0, -1, SEM_UNDO };
878  int intr = 0;
879 again:
880  if (semop(fSemaphore, &buf, 1) == -1) {
881 #if defined(R__FBSD) || defined(R__OBSD)
882  if (TSystem::GetErrno() == EINVAL)
883 #else
884  if (TSystem::GetErrno() == EIDRM)
885 #endif
886  fSemaphore = -1;
887 #if !defined(R__FBSD)
888  if (TSystem::GetErrno() == EINTR) {
889  if (intr > 2)
890  return -1;
891  TSystem::ResetErrno();
892  intr++;
893  goto again;
894  }
895 #endif
896  }
897  }
898 #else
899  // Enter Critical section to "write" lock
900  if (fSemaphore != (Int_t)INVALID_HANDLE_VALUE)
901  WaitForSingleObject((HANDLE)fhSemaphore,INFINITE);
902 #endif
903 #endif
904 
905  // file might have grown, update mapping on reader to new size
906  if (!fWritable && fMmallocDesc) {
907  if (mmalloc_update_mapping(fMmallocDesc) == -1)
908  Error("AcquireSemaphore", "cannot update mapping");
909  }
910 
911  return 0;
912 }
913 
914 ////////////////////////////////////////////////////////////////////////////////
915 /// Release semaphore. Returns 0 if OK, -1 on error.
916 
917 Int_t TMapFile::ReleaseSemaphore()
918 {
919 #ifdef HAVE_SEMOP
920 #ifndef WIN32
921  if (fSemaphore != -1) {
922  struct sembuf buf = { 0, 1, SEM_UNDO };
923  if (semop(fSemaphore, &buf, 1) == -1) {
924 #if defined(R__FBSD) || defined(R__OBSD)
925  if (TSystem::GetErrno() == EINVAL)
926 #else
927  if (TSystem::GetErrno() == EIDRM)
928 #endif
929  fSemaphore = -1;
930  }
931  }
932 #else
933  if (fSemaphore != (Int_t)INVALID_HANDLE_VALUE)
934  ReleaseMutex((HANDLE)fhSemaphore);
935 #endif
936 #endif
937  return 0;
938 }
939 
940 ////////////////////////////////////////////////////////////////////////////////
941 /// Close a mapped file.
942 ///
943 /// First detach mapped memory then close file.
944 /// No member functions of a TMapFile that was opened in write mode
945 /// may be called after Close() (this includes, of course, "delete" which
946 /// would call the dtors). The option="dtor" is only used when called
947 /// via the ~TMapFile.
948 
949 void TMapFile::Close(Option_t *option)
950 {
951  if (!fMmallocDesc) return;
952 
953  TMapFile *shadow = FindShadowMapFile();
954  if (!shadow) {
955  Error("Close", "shadow map == 0, should never happen!");
956  return;
957  }
958 
959  {
960  R__LOCKGUARD(gROOTMutex);
961  gROOT->GetListOfMappedFiles()->Remove(shadow);
962  gROOT->GetListOfMappedFiles()->Remove(this);
963  }
964 
965  if (shadow->fWritable) {
966  fWritable = kFALSE;
967  DeleteSemaphore();
968  }
969 
970  if (fMmallocDesc) {
971  if (strcmp(option, "dtor"))
972  mmalloc_detach(fMmallocDesc);
973 
974  // If writable cannot access fMmallocDesc anymore since
975  // it points to the just unmapped memory region. Any further
976  // access to this TMapFile will cause a crash.
977  if (!shadow->fWritable)
978  fMmallocDesc = 0;
979  }
980 
981  if (shadow->fFd != -1)
982 #ifndef WIN32
983  close(shadow->fFd);
984 #else
985  CloseHandle((HANDLE)shadow->fFd);
986 #endif
987 
988  delete shadow;
989 }
990 
991 ////////////////////////////////////////////////////////////////////////////////
992 /// Returns shadow map file.
993 
994 TMapFile *TMapFile::FindShadowMapFile()
995 {
996  R__LOCKGUARD(gROOTMutex);
997  TObjLink *lnk = ((TList *)gROOT->GetListOfMappedFiles())->LastLink();
998  while (lnk) {
999  TMapFile *mf = (TMapFile*)lnk->GetObject();
1000  if (mf->fVersion == -1 && fBaseAddr == mf->fBaseAddr && fSize == mf->fSize)
1001  return mf;
1002  lnk = lnk->Prev();
1003  }
1004  return 0;
1005 }
1006 
1007 ////////////////////////////////////////////////////////////////////////////////
1008 /// Print some info about the mapped file.
1009 
1010 void TMapFile::Print(Option_t *) const
1011 {
1012  Printf("Memory mapped file: %s", fName);
1013  Printf("Title: %s", fTitle);
1014  if (fMmallocDesc) {
1015  Printf("Option: %s", fOption);
1016  ULong_t size = (ULong_t)((struct mdesc *)fMmallocDesc)->top - fBaseAddr;
1017  Printf("Mapped Memory region: 0x%lx - 0x%lx (%.2f MB)", fBaseAddr, fBaseAddr + size,
1018  (float)size/1048576);
1019  Printf("Current breakval: 0x%lx", (ULong_t)GetBreakval());
1020  } else
1021  Printf("Option: file closed");
1022 }
1023 
1024 ////////////////////////////////////////////////////////////////////////////////
1025 /// Returns kTRUE in case object is a folder (i.e. contains browsable lists).
1026 
1027 Bool_t TMapFile::IsFolder() const
1028 {
1029  if (fMmallocDesc && fVersion > 0) return kTRUE;
1030  return kFALSE;
1031 }
1032 
1033 ////////////////////////////////////////////////////////////////////////////////
1034 /// Browse contents of TMapFile.
1035 
1036 void TMapFile::Browse(TBrowser *b)
1037 {
1038  if (b && fMmallocDesc) {
1039 
1040  AcquireSemaphore();
1041 
1042  TMapRec *mr = GetFirst();
1043  TKeyMapFile *keymap;
1044  if (!fBrowseList) fBrowseList = new TList();
1045  while (OrgAddress(mr)) {
1046  keymap = (TKeyMapFile*)fBrowseList->FindObject(mr->GetName(fOffset));
1047  if (!keymap) {
1048  keymap = new TKeyMapFile(mr->GetName(fOffset),mr->GetClassName(fOffset),this);
1049  fBrowseList->Add(keymap);
1050  }
1051  b->Add(keymap, keymap->GetName());
1052  mr = mr->GetNext(fOffset);
1053  }
1054 
1055  ReleaseSemaphore();
1056 
1057  }
1058 }
1059 
1060 ////////////////////////////////////////////////////////////////////////////////
1061 /// Cd to associated directory.
1062 
1063 Bool_t TMapFile::cd(const char *path)
1064 {
1065  if (fDirectory)
1066  return fDirectory->cd(path);
1067  return kFALSE;
1068 }
1069 
1070 ////////////////////////////////////////////////////////////////////////////////
1071 /// List contents of TMapFile.
1072 
1073 void TMapFile::ls(Option_t *) const
1074 {
1075  if (fMmallocDesc) {
1076 
1077  ((TMapFile*)this)->AcquireSemaphore();
1078 
1079  Printf("%-20s %-20s %-10s", "Object", "Class", "Size");
1080  if (!fFirst)
1081  Printf("*** no objects stored in memory mapped file ***");
1082 
1083  TMapRec *mr = GetFirst();
1084  while (OrgAddress(mr)) {
1085  Printf("%-20s %-20s %-10d", mr->GetName(fOffset),
1086  mr->GetClassName(fOffset), mr->fBufSize);
1087  mr = mr->GetNext(fOffset);
1088  }
1089 
1090  ((TMapFile*)this)->ReleaseSemaphore();
1091 
1092  }
1093 }
1094 
1095 ////////////////////////////////////////////////////////////////////////////////
1096 /// Increment statistics for buffer sizes of objects in this file.
1097 
1098 void TMapFile::SumBuffer(Int_t bufsize)
1099 {
1100  fWritten++;
1101  fSumBuffer += bufsize;
1102  fSum2Buffer += bufsize*bufsize;
1103 }
1104 
1105 ////////////////////////////////////////////////////////////////////////////////
1106 /// Return the best buffer size for objects in this file.
1107 ///
1108 /// The best buffer size is estimated based on the current mean value
1109 /// and standard deviation of all objects written so far to this file.
1110 /// Returns mean value + one standard deviation.
1111 
1112 Int_t TMapFile::GetBestBuffer()
1113 {
1114  if (!fWritten) return TBuffer::kMinimalSize;
1115  Double_t mean = fSumBuffer/fWritten;
1116  Double_t rms2 = TMath::Abs(fSum2Buffer/fSumBuffer - mean*mean);
1117  return (Int_t)(mean + std::sqrt(rms2));
1118 }
1119 
1120 ////////////////////////////////////////////////////////////////////////////////
1121 /// Return the current location in the memory region for this malloc heap which
1122 /// represents the end of memory in use. Returns 0 if map file was closed.
1123 
1124 void *TMapFile::GetBreakval() const
1125 {
1126  if (!fMmallocDesc) return 0;
1127  return (void *)((struct mdesc *)fMmallocDesc)->breakval;
1128 }
1129 
1130 ////////////////////////////////////////////////////////////////////////////////
1131 /// Create a memory mapped file.
1132 ///
1133 /// This opens a file (to which the
1134 /// memory will be mapped) and attaches a memory region to it.
1135 /// Option can be either: "NEW", "CREATE", "RECREATE", "UPDATE"
1136 /// or "READ" (see TFile). The default open mode is "READ". The size
1137 /// argument specifies the maximum size of shared memory file in bytes.
1138 /// TMapFile's can only be created via this method. Create() enforces that
1139 /// a TMapFile is always on the memory mapped heap (when "NEW", "CREATE"
1140 /// or "RECREATE" are used).
1141 
1142 TMapFile *TMapFile::Create(const char *name, Option_t *option, Int_t size,
1143  const char *title)
1144 {
1145  TMapFile *newMapFile;
1146  new TMapFile(name, title, option, size, newMapFile);
1147 
1148  return newMapFile;
1149 }
1150 
1151 ////////////////////////////////////////////////////////////////////////////////
1152 /// Set preferred map address.
1153 ///
1154 /// Find out preferred map address as follows:
1155 /// -# Run consumer program to find the preferred map address. Remember begin of mapped region, i.e. 0x40b4c000
1156 /// ~~~{.cpp}
1157 /// $ root
1158 /// root [0] m = TMapFile::Create("dummy.map", "recreate", 10000000);
1159 /// root [1] m.Print()
1160 /// Memory mapped file: dummy.map
1161 /// Title:
1162 /// Option: CREATE
1163 /// Mapped Memory region: 0x40b4c000 - 0x40d95f00 (2.29 MB)
1164 /// Current breakval: 0x40b53000
1165 /// root [2] .q
1166 /// $ rm dummy.map
1167 /// ~~~
1168 /// -# Add to producer program, just before creating the TMapFile:
1169 /// TMapFile::SetMapAddress(0x40b4c000);
1170 ///
1171 /// Repeat this if more than one map file is being used.
1172 /// The above procedure allow programs using, e.g., different number of
1173 /// shared libraries (that cause the default mapping address to be
1174 /// different) to create shared memory regions in the same location
1175 /// without overwriting a shared library. The above assumes the consumer
1176 /// program is larger (i.e. has more shared memory occupied) than the
1177 /// producer. If this is not true inverse the procedure.
1178 
1179 void TMapFile::SetMapAddress(Long_t addr)
1180 {
1181  fgMapAddress = addr;
1182 }
1183 
1184 ////////////////////////////////////////////////////////////////////////////////
1185 /// Return the base address at which we would like the next TMapFile's
1186 /// mapped data to start.
1187 ///
1188 /// For now, we let the system decide (start address 0). There are
1189 /// a lot of issues to deal with here to make this work reasonably,
1190 /// including:
1191 /// - Avoid memory collisions with existing mapped address spaces
1192 /// - Reclaim address spaces when their mmalloc heaps are unmapped
1193 /// - When mmalloc heaps are shared between processes they have to be
1194 /// mapped at the same addresses in each
1195 ///
1196 /// Once created, a mmalloc heap that is to be mapped back in must be
1197 /// mapped at the original address. I.e. each TMapFile will expect
1198 /// to be remapped at it's original address. This becomes a problem if
1199 /// the desired address is already in use.
1200 
1201 void *TMapFile::MapToAddress()
1202 {
1203 #ifdef R__HAVE_MMAP
1204  if (TStorage::HasCustomNewDelete())
1205  return (void *)fgMapAddress;
1206  else
1207  return (void *)-1;
1208 #else
1209  return (void *)-1;
1210 #endif
1211 }
1212 
1213 ////////////////////////////////////////////////////////////////////////////////
1214 /// Need special "operator delete" in which we close the shared memory.
1215 /// This has to be done after the dtor chain has been finished.
1216 
1217 void TMapFile::operator delete(void *ptr)
1218 {
1219  mmalloc_detach(fgMmallocDesc);
1220  fgMmallocDesc = 0;
1221 
1222  TObject::operator delete(ptr);
1223 }
1224 
1225 ////////////////////////////////////////////////////////////////////////////////
1226 
1227 TMapFile *TMapFile::WhichMapFile(void *addr)
1228 {
1229  if (!gROOT || !gROOT->GetListOfMappedFiles()) return 0;
1230 
1231  TObjLink *lnk = ((TList *)gROOT->GetListOfMappedFiles())->LastLink();
1232  while (lnk) {
1233  TMapFile *mf = (TMapFile*)lnk->GetObject();
1234  if (!mf) return 0;
1235  if ((ULong_t)addr >= mf->fBaseAddr + mf->fOffset &&
1236  (ULong_t)addr < (ULong_t)mf->GetBreakval() + mf->fOffset)
1237  return mf;
1238  lnk = lnk->Prev();
1239  }
1240  return 0;
1241 }
1242