1 // $Id: Filecache.cpp 80826 2008-03-04 14:51:23Z wotte $
3 #include "ace/Filecache.h"
4 #include "ace/Object_Manager.h"
5 #include "ace/Log_Msg.h"
7 #include "ace/Guard_T.h"
8 #include "ace/OS_NS_string.h"
9 #include "ace/OS_NS_time.h"
10 #include "ace/OS_NS_unistd.h"
11 #include "ace/OS_NS_fcntl.h"
12 #include "ace/Truncate.h"
16 "$Id: Filecache.cpp 80826 2008-03-04 14:51:23Z wotte $")
18 #if defined (ACE_WIN32)
19 // Specifies no sharing flags.
20 #define R_MASK ACE_DEFAULT_OPEN_PERMS
23 #define R_MASK S_IRUSR|S_IRGRP|S_IROTH
24 #define W_MASK S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH
25 #endif /* ACE_WIN32 */
27 #if defined (ACE_WIN32)
28 // See if you can get rid of some of these.
29 #define READ_FLAGS (FILE_FLAG_SEQUENTIAL_SCAN | \
30 FILE_FLAG_OVERLAPPED | \
32 // static const int RCOPY_FLAGS = (FILE_FLAG_SEQUENTIAL_SCAN |
34 #define WRITE_FLAGS (FILE_FLAG_SEQUENTIAL_SCAN | \
35 FILE_FLAG_OVERLAPPED | \
36 O_RDWR | O_CREAT | O_TRUNC)
37 // static const int WCOPY_FLAGS = (FILE_FLAG_SEQUENTIAL_SCAN |
38 // O_RDWR | O_CREAT | O_TRUNC);
40 #define READ_FLAGS O_RDONLY
41 // static const int RCOPY_FLAGS = O_RDONLY;
42 #define WRITE_FLAGS (O_RDWR | O_CREAT | O_TRUNC)
43 // static const int WCOPY_FLAGS = O_RDWR | O_CREAT | O_TRUNC;
44 #endif /* ACE_WIN32 */
46 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
48 // static data members
49 ACE_Filecache
*ACE_Filecache::cvf_
= 0;
52 ACE_Filecache_Handle::init (void)
55 this->handle_
= ACE_INVALID_HANDLE
;
58 ACE_Filecache_Handle::ACE_Filecache_Handle (void)
59 : file_ (0), handle_ (0), mapit_ (0)
64 ACE_Filecache_Handle::ACE_Filecache_Handle (const ACE_TCHAR
*filename
,
65 ACE_Filecache_Flag mapit
)
66 : file_ (0), handle_ (0), mapit_ (mapit
)
69 // Fetch the file from the Virtual_Filesystem let the
70 // Virtual_Filesystem do the work of cache coherency.
72 // Filecache will also do the acquire, since it holds the lock at
74 this->file_
= ACE_Filecache::instance ()->fetch (filename
, mapit
);
77 ACE_Filecache_Handle::ACE_Filecache_Handle (const ACE_TCHAR
*filename
,
79 ACE_Filecache_Flag mapit
)
80 : file_ (0), handle_ (0), mapit_ (mapit
)
85 ACE_Filecache::instance ()->remove (filename
);
88 // Since this is being opened for a write, simply create a new
89 // ACE_Filecache_Object now, and let the destructor add it into CVF
92 // Filecache will also do the acquire, since it holds the lock at
94 this->file_
= ACE_Filecache::instance ()->create (filename
, size
);
98 ACE_Filecache_Handle::~ACE_Filecache_Handle (void)
100 if (this->handle_
!= ACE_INVALID_HANDLE
)
102 ACE_OS::close (this->handle_
);
104 ACE_Filecache::instance ()->finish (this->file_
);
108 ACE_Filecache_Handle::address (void) const
110 return this->file_
== 0 ? 0 : this->file_
->address ();
114 ACE_Filecache_Handle::handle (void) const
116 if (this->handle_
== ACE_INVALID_HANDLE
&& this->file_
!= 0)
118 ACE_Filecache_Handle
*mutable_this
=
119 const_cast<ACE_Filecache_Handle
*> (this);
120 mutable_this
->handle_
= ACE_OS::dup (this->file_
->handle ());
122 return this->handle_
;
126 ACE_Filecache_Handle::error (void) const
128 if (this->file_
== 0)
131 return this->file_
->error ();
135 ACE_Filecache_Handle::size (void) const
137 if (this->file_
== 0)
140 return this->file_
->size ();
143 // ------------------
144 // ACE_Filecache_Hash
145 // ------------------
147 #define ACE_Filecache_Hash \
148 ACE_Hash_Map_Manager_Ex<const ACE_TCHAR *, ACE_Filecache_Object *, ACE_Hash<const ACE_TCHAR *>, ACE_Equal_To<const ACE_TCHAR *>, ACE_Null_Mutex>
149 #define ACE_Filecache_Hash_Entry \
150 ACE_Hash_Map_Entry<const ACE_TCHAR *, ACE_Filecache_Object *>
153 ACE_Filecache_Hash_Entry::ACE_Hash_Map_Entry (
154 const ACE_TCHAR
*const &ext_id
,
155 ACE_Filecache_Object
*const &int_id
,
156 ACE_Filecache_Hash_Entry
*next
,
157 ACE_Filecache_Hash_Entry
*prev
)
159 ? ACE_OS::strdup (ext_id
)
160 : ACE_OS::strdup (ACE_TEXT (""))),
168 ACE_Filecache_Hash_Entry::ACE_Hash_Map_Entry (ACE_Filecache_Hash_Entry
*next
,
169 ACE_Filecache_Hash_Entry
*prev
)
177 ACE_Filecache_Hash_Entry::~ACE_Hash_Map_Entry (void)
179 ACE_OS::free ((void *) ext_id_
);
182 // We need these template specializations since KEY is defined as a
183 // ACE_TCHAR*, which doesn't have a hash() or equal() method defined on it.
187 ACE_Filecache_Hash::hash (const ACE_TCHAR
*const &ext_id
)
189 return ACE::hash_pjw (ext_id
);
194 ACE_Filecache_Hash::equal (const ACE_TCHAR
*const &id1
,
195 const ACE_TCHAR
*const &id2
)
197 return ACE_OS::strcmp (id1
, id2
) == 0;
200 #undef ACE_Filecache_Hash
201 #undef ACE_Filecache_Hash_Entry
209 ACE_Filecache::instance (void)
211 // Double check locking pattern.
212 if (ACE_Filecache::cvf_
== 0)
214 ACE_SYNCH_RW_MUTEX
&lock
=
215 *ACE_Managed_Object
<ACE_SYNCH_RW_MUTEX
>::get_preallocated_object
216 (ACE_Object_Manager::ACE_FILECACHE_LOCK
);
217 ACE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX
, ace_mon
, lock
, 0);
219 // @@ James, please check each of the ACE_NEW_RETURN calls to
220 // make sure that it is safe to return if allocation fails.
221 if (ACE_Filecache::cvf_
== 0)
222 ACE_NEW_RETURN (ACE_Filecache::cvf_
,
227 return ACE_Filecache::cvf_
;
230 ACE_Filecache::ACE_Filecache (void)
231 : size_ (ACE_DEFAULT_VIRTUAL_FILESYSTEM_TABLE_SIZE
),
236 ACE_Filecache::~ACE_Filecache (void)
240 ACE_Filecache_Object
*
241 ACE_Filecache::insert_i (const ACE_TCHAR
*filename
,
242 ACE_SYNCH_RW_MUTEX
&filelock
,
245 ACE_Filecache_Object
*handle
= 0;
247 if (this->hash_
.find (filename
, handle
) == -1)
249 ACE_NEW_RETURN (handle
,
250 ACE_Filecache_Object (filename
, filelock
, 0, mapit
),
253 // ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) CVF: creating %s\n"), filename));
255 if (this->hash_
.bind (filename
, handle
) == -1)
267 ACE_Filecache_Object
*
268 ACE_Filecache::remove_i (const ACE_TCHAR
*filename
)
270 ACE_Filecache_Object
*handle
= 0;
272 // Disassociate file from the cache.
273 if (this->hash_
.unbind (filename
, handle
) == 0)
277 // Try a lock. If it succeeds, we can delete it now.
278 // Otherwise, it will clean itself up later.
279 if (handle
->lock_
.tryacquire_write () == 0)
291 ACE_Filecache_Object
*
292 ACE_Filecache::update_i (const ACE_TCHAR
*filename
,
293 ACE_SYNCH_RW_MUTEX
&filelock
,
296 ACE_Filecache_Object
*handle
= 0;
298 handle
= this->remove_i (filename
);
299 handle
= this->insert_i (filename
, filelock
, mapit
);
305 ACE_Filecache::find (const ACE_TCHAR
*filename
)
307 return this->hash_
.find (filename
);
311 ACE_Filecache_Object
*
312 ACE_Filecache::remove (const ACE_TCHAR
*filename
)
314 ACE_Filecache_Object
*handle
= 0;
316 ACE_OFF_T loc
= ACE::hash_pjw (filename
) % this->size_
;
317 ACE_SYNCH_RW_MUTEX
&hashlock
= this->hash_lock_
[loc
];
318 // ACE_SYNCH_RW_MUTEX &filelock = this->file_lock_[loc];
320 if (this->hash_
.find (filename
, handle
) != -1)
322 ACE_WRITE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX
,
327 return this->remove_i (filename
);
334 ACE_Filecache_Object
*
335 ACE_Filecache::fetch (const ACE_TCHAR
*filename
, int mapit
)
337 ACE_Filecache_Object
*handle
= 0;
339 ACE_OFF_T loc
= ACE::hash_pjw (filename
) % this->size_
;
340 ACE_SYNCH_RW_MUTEX
&hashlock
= this->hash_lock_
[loc
];
341 ACE_SYNCH_RW_MUTEX
&filelock
= this->file_lock_
[loc
];
343 filelock
.acquire_read ();
345 if (this->hash_
.find (filename
, handle
) == -1)
347 ACE_WRITE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX
,
352 // Second check in the method call
353 handle
= this->insert_i (filename
, filelock
, mapit
);
360 if (handle
->update ())
363 // Double check locking pattern
364 ACE_WRITE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX
,
369 // Second check in the method call
370 handle
= this->update_i (filename
, filelock
, mapit
);
376 // ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) CVF: found %s\n"), filename));
382 ACE_Filecache_Object
*
383 ACE_Filecache::create (const ACE_TCHAR
*filename
, int size
)
385 ACE_Filecache_Object
*handle
= 0;
387 ACE_OFF_T loc
= ACE::hash_pjw (filename
) % this->size_
;
388 ACE_SYNCH_RW_MUTEX
&filelock
= this->file_lock_
[loc
];
390 ACE_NEW_RETURN (handle
,
391 ACE_Filecache_Object (filename
, size
, filelock
),
398 ACE_Filecache_Object
*
399 ACE_Filecache::finish (ACE_Filecache_Object
*&file
)
404 ACE_OFF_T loc
= ACE::hash_pjw (file
->filename_
) % this->size_
;
405 ACE_SYNCH_RW_MUTEX
&hashlock
= this->hash_lock_
[loc
];
408 switch (file
->action_
)
410 case ACE_Filecache_Object::ACE_WRITING
:
412 ACE_WRITE_GUARD_RETURN (ACE_SYNCH_RW_MUTEX
,
419 this->remove_i (file
->filename_
);
421 int result
= this->hash_
.bind (file
->filename (), file
);
426 // Last one using a stale file is resposible for deleting it.
429 // Try a lock. If it succeds, we can delete it now.
430 // Otherwise, it will clean itself up later.
431 if (file
->lock_
.tryacquire_write () == 0)
444 // Last one using a stale file is resposible for deleting it.
447 // Try a lock. If it succeds, we can delete it now.
448 // Otherwise, it will clean itself up later.
449 if (file
->lock_
.tryacquire_write () == 0)
463 ACE_Filecache_Object::init (void)
465 this->filename_
[0] = '\0';
466 this->handle_
= ACE_INVALID_HANDLE
;
467 this->error_
= ACE_SUCCESS
;
471 ACE_OS::memset (&(this->stat_
), 0, sizeof (this->stat_
));
474 ACE_Filecache_Object::ACE_Filecache_Object (void)
490 ACE_Filecache_Object::ACE_Filecache_Object (const ACE_TCHAR
*filename
,
491 ACE_SYNCH_RW_MUTEX
&lock
,
492 LPSECURITY_ATTRIBUTES sa
,
508 // ASSERT strlen(filename) < sizeof (this->filename_)
509 ACE_OS::strcpy (this->filename_
, filename
);
510 this->action_
= ACE_Filecache_Object::ACE_READING
;
511 // place ourselves into the READING state
513 // Can we access the file?
514 if (ACE_OS::access (this->filename_
, R_OK
) == -1)
516 this->error_i (ACE_Filecache_Object::ACE_ACCESS_FAILED
);
520 // Can we stat the file?
521 if (ACE_OS::stat (this->filename_
, &this->stat_
) == -1)
523 this->error_i (ACE_Filecache_Object::ACE_STAT_FAILED
);
527 this->size_
= ACE_Utils::truncate_cast
<ACE_OFF_T
> (this->stat_
.st_size
);
528 this->tempname_
= this->filename_
;
530 // Can we open the file?
531 this->handle_
= ACE_OS::open (this->tempname_
,
532 READ_FLAGS
, R_MASK
, this->sa_
);
533 if (this->handle_
== ACE_INVALID_HANDLE
)
535 this->error_i (ACE_Filecache_Object::ACE_OPEN_FAILED
,
536 ACE_TEXT ("ACE_Filecache_Object::ctor: open"));
542 // Can we map the file?
543 if (this->mmap_
.map (this->handle_
, static_cast<size_t> (-1),
544 PROT_READ
, ACE_MAP_PRIVATE
, 0, 0, this->sa_
) != 0)
546 this->error_i (ACE_Filecache_Object::ACE_MEMMAP_FAILED
,
547 ACE_TEXT ("ACE_Filecache_Object::ctor: map"));
548 ACE_OS::close (this->handle_
);
549 this->handle_
= ACE_INVALID_HANDLE
;
555 this->action_
= ACE_Filecache_Object::ACE_READING
;
558 ACE_Filecache_Object::ACE_Filecache_Object (const ACE_TCHAR
*filename
,
560 ACE_SYNCH_RW_MUTEX
&lock
,
561 LPSECURITY_ATTRIBUTES sa
)
569 ACE_OS::strcpy (this->filename_
, filename
);
570 this->action_
= ACE_Filecache_Object::ACE_WRITING
;
572 // Can we access the file?
573 if (ACE_OS::access (this->filename_
, R_OK
|W_OK
) == -1
575 && ACE_OS::access (this->filename_
, F_OK
) != -1)
577 // File exists, but we cannot access it.
578 this->error_i (ACE_Filecache_Object::ACE_ACCESS_FAILED
);
582 this->tempname_
= this->filename_
;
584 // Can we open the file?
585 this->handle_
= ACE_OS::open (this->tempname_
, WRITE_FLAGS
, W_MASK
, this->sa_
);
586 if (this->handle_
== ACE_INVALID_HANDLE
)
588 this->error_i (ACE_Filecache_Object::ACE_OPEN_FAILED
,
589 ACE_TEXT ("ACE_Filecache_Object::acquire: open"));
594 if (ACE_OS::pwrite (this->handle_
, "", 1, this->size_
- 1) != 1)
596 this->error_i (ACE_Filecache_Object::ACE_WRITE_FAILED
,
597 ACE_TEXT ("ACE_Filecache_Object::acquire: write"));
598 ACE_OS::close (this->handle_
);
603 if (this->mmap_
.map (this->handle_
, this->size_
, PROT_RDWR
, MAP_SHARED
,
604 0, 0, this->sa_
) != 0)
606 this->error_i (ACE_Filecache_Object::ACE_MEMMAP_FAILED
,
607 ACE_TEXT ("ACE_Filecache_Object::acquire: map"));
608 ACE_OS::close (this->handle_
);
614 ACE_Filecache_Object::~ACE_Filecache_Object (void)
616 if (this->error_
== ACE_SUCCESS
)
618 this->mmap_
.unmap ();
619 ACE_OS::close (this->handle_
);
620 this->handle_
= ACE_INVALID_HANDLE
;
623 this->lock_
.release ();
627 ACE_Filecache_Object::acquire (void)
629 return this->lock_
.tryacquire_read ();
633 ACE_Filecache_Object::release (void)
635 if (this->action_
== ACE_WRITING
)
637 // We are safe since only one thread has a writable Filecache_Object
640 ACE_HANDLE original
= ACE_OS::open (this->filename_
, WRITE_FLAGS
, W_MASK
,
642 if (original
== ACE_INVALID_HANDLE
)
643 this->error_
= ACE_Filecache_Object::ACE_OPEN_FAILED
;
644 else if (ACE_OS::write (original
, this->mmap_
.addr (),
647 this->error_
= ACE_Filecache_Object::ACE_WRITE_FAILED
;
648 ACE_OS::close (original
);
649 ACE_OS::unlink (this->filename_
);
651 else if (ACE_OS::stat (this->filename_
, &this->stat_
) == -1)
652 this->error_
= ACE_Filecache_Object::ACE_STAT_FAILED
;
655 this->mmap_
.unmap ();
656 ACE_OS::close (this->handle_
);
657 this->handle_
= ACE_INVALID_HANDLE
;
660 // Leave the file in an acquirable state.
661 this->handle_
= ACE_OS::open (this->tempname_
, READ_FLAGS
, R_MASK
);
662 if (this->handle_
== ACE_INVALID_HANDLE
)
664 this->error_i (ACE_Filecache_Object::ACE_OPEN_FAILED
,
665 "ACE_Filecache_Object::acquire: open");
667 else if (this->mmap_
.map (this->handle_
, -1,
674 this->error_i (ACE_Filecache_Object::ACE_MEMMAP_FAILED
,
675 "ACE_Filecache_Object::acquire: map");
676 ACE_OS::close (this->handle_
);
677 this->handle_
= ACE_INVALID_HANDLE
;
680 this->action_
= ACE_Filecache_Object::ACE_READING
;
684 return this->lock_
.release ();
688 ACE_Filecache_Object::error (void) const
690 // The existence of the object means a read lock is being held.
695 ACE_Filecache_Object::error_i (int error_value
, const ACE_TCHAR
*s
)
698 ACE_ERROR ((LM_ERROR
, ACE_TEXT ("%p.\n"), s
));
699 this->error_
= error_value
;
704 ACE_Filecache_Object::filename (void) const
706 // The existence of the object means a read lock is being held.
707 return this->filename_
;
711 ACE_Filecache_Object::size (void) const
713 // The existence of the object means a read lock is being held.
718 ACE_Filecache_Object::handle (void) const
720 // The existence of the object means a read lock is being held.
721 return this->handle_
;
725 ACE_Filecache_Object::address (void) const
727 // The existence of the object means a read lock is being held.
728 return this->mmap_
.addr ();
732 ACE_Filecache_Object::update (void) const
734 // The existence of the object means a read lock is being held.
738 if (ACE_OS::stat (this->filename_
, &statbuf
) == -1)
741 // non-portable code may follow
742 #if defined (ACE_HAS_WINCE)
743 // Yup, non-portable... there's probably a way to safely implement
744 // difftime() on WinCE, but for now, this will have to do. It flags
745 // every file as having changed since cached.
748 result
= ACE_OS::difftime (this->stat_
.st_mtime
, statbuf
.st_mtime
) < 0;
749 #endif /* ACE_HAS_WINCE */
754 ACE_END_VERSIONED_NAMESPACE_DECL