/inotify-cxx/trunk/TODO |
---|
1,3 → 1,3 |
Currently pending tasks: |
*** nothing to do *** |
#0000093 - optional thread safety |
/inotify-cxx/trunk/CHANGELOG |
---|
1,8 → 1,3 |
0.5.0 2006-10-29 |
* partial thread safety has been implemented (using rwlocks) |
* Inotify::GetEnabledCount() method has been added |
0.4.1 2006-10-14 |
* wrong value returned by Inotify::GetWatchCount() has been fixed |
(#0000092) |
/inotify-cxx/trunk/inotify-cxx.h |
---|
50,89 → 50,8 |
*/ |
#define IN_EXC_MSG(msg) (std::string(__PRETTY_FUNCTION__) + ": " + msg) |
/// inotify-cxx thread safety |
/** |
* If this symbol is defined you can use this interface safely |
* threaded applications. Remember that it slightly degrades |
* performance. |
* |
* Even if INOTIFY_THREAD_SAFE is defined some classes stay |
* unsafe. If you must use them (must you?) in more than one |
* thread concurrently you need to implement explicite locking. |
* |
* You need not to define INOTIFY_THREAD_SAFE in that cases |
* where the application is multithreaded but all the inotify |
* infrastructure will be managed only in one thread. This is |
* the recommended way. |
* |
* Locking may fail (it is very rare but not impossible). In this |
* case an exception is thrown. But if unlocking fails in case |
* of an error it does nothing (this failure is ignored). |
*/ |
#ifdef INOTIFY_THREAD_SAFE |
#include <pthread.h> |
#define IN_LOCK_DECL mutable pthread_rwlock_t __m_lock; |
#define IN_LOCK_INIT \ |
{ \ |
pthread_rwlockattr_t attr; \ |
int res = 0; \ |
if ((res = pthread_rwlockattr_init(&attr)) != 0) \ |
throw InotifyException(IN_EXC_MSG("cannot initialize lock attributes"), res, this); \ |
if ((res = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP)) != 0) \ |
throw InotifyException(IN_EXC_MSG("cannot set lock kind"), res, this); \ |
if ((res = pthread_rwlock_init(&__m_lock, &attr)) != 0) \ |
throw InotifyException(IN_EXC_MSG("cannot initialize lock"), res, this); \ |
pthread_rwlockattr_destroy(&attr); \ |
} |
#define IN_LOCK_DONE pthread_rwlock_destroy(&__m_lock); |
#define IN_READ_BEGIN \ |
{ \ |
int res = pthread_rwlock_rdlock(&__m_lock); \ |
if (res != 0) \ |
throw InotifyException(IN_EXC_MSG("locking for reading failed"), res, (void*) this); \ |
} |
#define IN_READ_END \ |
{ \ |
int res = pthread_rwlock_unlock(&__m_lock); \ |
if (res != 0) \ |
throw InotifyException(IN_EXC_MSG("unlocking failed"), res, (void*) this); \ |
} |
#define IN_READ_END_NOTHROW pthread_rwlock_unlock(&__m_lock); |
#define IN_WRITE_BEGIN \ |
{ \ |
int res = pthread_rwlock_wrlock(&__m_lock); \ |
if (res != 0) \ |
throw InotifyException(IN_EXC_MSG("locking for writing failed"), res, (void*) this); \ |
} |
#define IN_WRITE_END IN_READ_END |
#define IN_WRITE_END_NOTHROW IN_READ_END_NOTHROW |
#else // INOTIFY_THREAD_SAFE |
#define IN_LOCK_DECL |
#define IN_LOCK_INIT |
#define IN_LOCK_DONE |
#define IN_READ_BEGIN |
#define IN_READ_END |
#define IN_READ_END_NOTHROW |
#define IN_WRITE_BEGIN |
#define IN_WRITE_END |
#define IN_WRITE_END_NOTHROW |
#endif // INOTIFY_THREAD_SAFE |
// forward declaration |
class InotifyWatch; |
class Inotify; |
139,14 → 58,6 |
/// Class for inotify exceptions |
/** |
* This class allows to acquire information about exceptional |
* events. It makes easier to log or display error messages |
* and to identify problematic code locations. |
* |
* Although this class is basically thread-safe it is not intended |
* to be shared between threads. |
*/ |
class InotifyException |
{ |
public: |
203,10 → 114,6 |
/** |
* It holds all information about inotify event and provides |
* access to its particular values. |
* |
* This class is not (and is not intended to be) thread-safe |
* and therefore it must not be used concurrently in multiple |
* threads. |
*/ |
class InotifyEvent |
{ |
364,12 → 271,6 |
/// inotify watch class |
/** |
* It holds information about the inotify watch on a particular |
* inode. |
* |
* If the INOTIFY_THREAD_SAFE is defined this class is thread-safe. |
*/ |
class InotifyWatch |
{ |
public: |
388,14 → 289,11 |
m_wd((int32_t) -1), |
m_fEnabled(fEnabled) |
{ |
IN_LOCK_INIT |
} |
/// Destructor. |
~InotifyWatch() |
{ |
IN_LOCK_DONE |
} |
~InotifyWatch() {} |
/// Returns the watch descriptor. |
/** |
426,7 → 324,7 |
/// Sets the watch event mask. |
/** |
* If the watch is active (added to an instance of Inotify) |
* If the watch is active (added to an instance of Inofify) |
* this method may fail due to unsuccessful re-setting |
* the watch in the kernel. |
* |
447,7 → 345,7 |
/// Enables/disables the watch. |
/** |
* If the watch is active (added to an instance of Inotify) |
* If the watch is active (added to an instance of Inofify) |
* this method may fail due to unsuccessful re-setting |
* the watch in the kernel. |
* |
476,8 → 374,6 |
int32_t m_wd; ///< watch descriptor |
Inotify* m_pInotify; ///< inotify object |
bool m_fEnabled; ///< events enabled yes/no |
IN_LOCK_DECL |
}; |
489,12 → 385,6 |
/// inotify class |
/** |
* It holds information about the inotify device descriptor |
* and manages the event queue. |
* |
* If the INOTIFY_THREAD_SAFE is defined this class is thread-safe. |
*/ |
class Inotify |
{ |
public: |
567,31 → 457,12 |
* enabled or not). |
* |
* \return count of watches |
* |
* \sa GetEnabledCount() |
*/ |
inline size_t GetWatchCount() const |
{ |
IN_READ_BEGIN |
size_t n = (size_t) m_paths.size(); |
IN_READ_END |
return n; |
return (size_t) m_paths.size(); |
} |
/// Returns the count of enabled watches. |
/** |
* \return count of enabled watches |
* |
* \sa GetWatchCount() |
*/ |
inline size_t GetEnabledCount() const |
{ |
IN_READ_BEGIN |
size_t n = (size_t) m_watches.size(); |
IN_READ_END |
return n; |
} |
/// Waits for inotify events. |
/** |
* It waits until one or more events occur. When called |
613,13 → 484,7 |
* |
* \return count of events |
*/ |
inline size_t GetEventCount() |
{ |
IN_READ_BEGIN |
size_t n = (size_t) m_events.size(); |
IN_READ_END |
return n; |
} |
int GetEventCount(); |
/// Extracts a queued inotify event. |
/** |
726,11 → 591,8 |
unsigned char m_buf[INOTIFY_BUFLEN]; ///< buffer for events |
std::deque<InotifyEvent> m_events; ///< event queue |
IN_LOCK_DECL |
friend class InotifyWatch; |
}; |
#endif //_INOTIFYCXX_H_ |
/inotify-cxx/trunk/inotify-cxx.cpp |
---|
203,44 → 203,30 |
void InotifyWatch::SetMask(uint32_t uMask) throw (InotifyException) |
{ |
IN_WRITE_BEGIN |
if (m_wd != -1) { |
int wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), uMask); |
if (wd != m_wd) { |
IN_WRITE_END_NOTHROW |
if (wd != m_wd) |
throw InotifyException(IN_EXC_MSG("changing mask failed"), wd == -1 ? errno : EINVAL, this); |
} |
} |
m_uMask = uMask; |
IN_WRITE_END |
} |
void InotifyWatch::SetEnabled(bool fEnabled) throw (InotifyException) |
{ |
IN_WRITE_BEGIN |
if (fEnabled == m_fEnabled) { |
IN_WRITE_END_NOTHROW |
if (fEnabled == m_fEnabled) |
return; |
} |
if (m_pInotify != NULL) { |
if (fEnabled) { |
m_wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), m_uMask); |
if (m_wd == -1) { |
IN_WRITE_END_NOTHROW |
if (m_wd == -1) |
throw InotifyException(IN_EXC_MSG("enabling watch failed"), errno, this); |
} |
m_pInotify->m_watches.insert(IN_WATCH_MAP::value_type(m_wd, this)); |
} |
else { |
if (inotify_rm_watch(m_pInotify->GetDescriptor(), m_wd) != 0) { |
IN_WRITE_END_NOTHROW |
if (inotify_rm_watch(m_pInotify->GetDescriptor(), m_wd) != 0) |
throw InotifyException(IN_EXC_MSG("disabling watch failed"), errno, this); |
} |
m_pInotify->m_watches.erase(m_wd); |
m_wd = -1; |
} |
247,57 → 233,39 |
} |
m_fEnabled = fEnabled; |
IN_WRITE_END |
} |
Inotify::Inotify() throw (InotifyException) |
{ |
IN_LOCK_INIT |
m_fd = inotify_init(); |
if (m_fd == -1) { |
IN_LOCK_DONE |
if (m_fd == -1) |
throw InotifyException(IN_EXC_MSG("inotify init failed"), errno, NULL); |
} |
} |
Inotify::~Inotify() |
{ |
Close(); |
IN_LOCK_DONE |
} |
void Inotify::Close() |
{ |
IN_WRITE_BEGIN |
if (m_fd != -1) { |
RemoveAll(); |
close(m_fd); |
m_fd = -1; |
} |
IN_WRITE_END |
} |
void Inotify::Add(InotifyWatch* pWatch) throw (InotifyException) |
{ |
IN_WRITE_BEGIN |
// invalid descriptor - this case shouldn't occur - go away |
if (m_fd == -1) { |
IN_WRITE_END_NOTHROW |
if (m_fd == -1) |
throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this); |
} |
// this path already watched - go away |
if (FindWatch(pWatch->GetPath()) != NULL) { |
IN_WRITE_END_NOTHROW |
if (FindWatch(pWatch->GetPath()) != NULL) |
throw InotifyException(IN_EXC_MSG("path already watched"), EBUSY, this); |
} |
// for enabled watch |
if (pWatch->IsEnabled()) { |
306,10 → 274,8 |
int wd = inotify_add_watch(m_fd, pWatch->GetPath().c_str(), pWatch->GetMask()); |
// adding failed - go away |
if (wd == -1) { |
IN_WRITE_END_NOTHROW |
if (wd == -1) |
throw InotifyException(IN_EXC_MSG("adding watch failed"), errno, this); |
} |
// this path already watched (but defined another way) |
InotifyWatch* pW = FindWatch(wd); |
317,12 → 283,10 |
// try to recover old watch because it may be modified - then go away |
if (inotify_add_watch(m_fd, pW->GetPath().c_str(), pW->GetMask()) < 0) { |
IN_WRITE_END_NOTHROW |
throw InotifyException(IN_EXC_MSG("watch collision detected and recovery failed"), errno, this); |
} |
else { |
// recovery failed - go away |
IN_WRITE_END_NOTHROW |
throw InotifyException(IN_EXC_MSG("path already watched (but defined another way)"), EBUSY, this); |
} |
} |
333,28 → 297,20 |
m_paths.insert(IN_WP_MAP::value_type(pWatch->m_path, pWatch)); |
pWatch->m_pInotify = this; |
IN_WRITE_END |
} |
void Inotify::Remove(InotifyWatch* pWatch) throw (InotifyException) |
{ |
IN_WRITE_BEGIN |
// invalid descriptor - this case shouldn't occur - go away |
if (m_fd == -1) { |
IN_WRITE_END_NOTHROW |
if (m_fd == -1) |
throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this); |
} |
// for enabled watch |
if (pWatch->m_wd != -1) { |
// removing watch failed - go away |
if (inotify_rm_watch(m_fd, pWatch->m_wd) == -1) { |
IN_WRITE_END_NOTHROW |
if (inotify_rm_watch(m_fd, pWatch->m_wd) == -1) |
throw InotifyException(IN_EXC_MSG("removing watch failed"), errno, this); |
} |
m_watches.erase(pWatch->m_wd); |
pWatch->m_wd = -1; |
} |
361,14 → 317,10 |
m_paths.erase(pWatch->m_path); |
pWatch->m_pInotify = NULL; |
IN_WRITE_END |
} |
void Inotify::RemoveAll() |
{ |
IN_WRITE_BEGIN |
IN_WP_MAP::iterator it = m_paths.begin(); |
while (it != m_paths.end()) { |
InotifyWatch* pW = (*it).second; |
382,8 → 334,6 |
m_watches.clear(); |
m_paths.clear(); |
IN_WRITE_END |
} |
void Inotify::WaitForEvents(bool fNoIntr) throw (InotifyException) |
400,8 → 350,6 |
if (len < 0) |
throw InotifyException(IN_EXC_MSG("reading events failed"), errno, this); |
IN_WRITE_BEGIN |
ssize_t i = 0; |
while (i < len) { |
struct inotify_event* pEvt = (struct inotify_event*) &m_buf[i]; |
412,24 → 360,19 |
} |
i += INOTIFY_EVENT_SIZE + (ssize_t) pEvt->len; |
} |
} |
IN_WRITE_END |
int Inotify::GetEventCount() |
{ |
return m_events.size(); |
} |
bool Inotify::GetEvent(InotifyEvent* pEvt) throw (InotifyException) |
{ |
if (pEvt == NULL) |
throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this); |
bool b = PeekEvent(pEvt); |
IN_WRITE_BEGIN |
bool b = !m_events.empty(); |
if (b) { |
*pEvt = m_events.front(); |
if (b) |
m_events.pop_front(); |
} |
IN_WRITE_END |
return b; |
} |
439,56 → 382,40 |
if (pEvt == NULL) |
throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this); |
IN_READ_BEGIN |
bool b = !m_events.empty(); |
if (b) { |
if (!m_events.empty()) { |
*pEvt = m_events.front(); |
return true; |
} |
IN_READ_END |
return b; |
return false; |
} |
InotifyWatch* Inotify::FindWatch(int iDescriptor) |
{ |
IN_READ_BEGIN |
IN_WATCH_MAP::iterator it = m_watches.find(iDescriptor); |
InotifyWatch* pW = it == m_watches.end() ? NULL : (*it).second; |
IN_READ_END |
return pW; |
if (it == m_watches.end()) |
return NULL; |
return (*it).second; |
} |
InotifyWatch* Inotify::FindWatch(const std::string& rPath) |
{ |
IN_READ_BEGIN |
IN_WP_MAP::iterator it = m_paths.find(rPath); |
InotifyWatch* pW = it == m_paths.end() ? NULL : (*it).second; |
IN_READ_END |
if (it == m_paths.end()) |
return NULL; |
return pW; |
return (*it).second; |
} |
void Inotify::SetNonBlock(bool fNonBlock) throw (InotifyException) |
{ |
IN_WRITE_BEGIN |
if (m_fd == -1) { |
IN_WRITE_END_NOTHROW |
if (m_fd == -1) |
throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this); |
} |
int res = fcntl(m_fd, F_GETFL); |
if (res == -1) { |
IN_WRITE_END_NOTHROW |
if (res == -1) |
throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this); |
} |
if (fNonBlock) { |
res |= O_NONBLOCK; |
497,11 → 424,7 |
res &= ~O_NONBLOCK; |
} |
if (fcntl(m_fd, F_SETFL, res) == -1) { |
IN_WRITE_END_NOTHROW |
if (fcntl(m_fd, F_SETFL, res) == -1) |
throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this); |
} |
IN_WRITE_END |
} |
/inotify-cxx/trunk/README |
---|
40,11 → 40,7 |
/usr/include), use #include <inotify-cxx.h> or similar. |
Otherwise use #include "inotify-cxx.h". |
For thread-safe behavior, define the INOTIFY_THREAD_SAFE symbol |
(eg. -DINOTIFY_THREAD_SAFE on gcc's command line). See documentation |
for details about thread safety. |
4. Bugs, suggestions |
THIS PROGRAM IS AN ALPHA VERSION. IT PROBABLY CONTAINS BUGS AND |
THEREFORE IT IS NOT INTENDED FOR PRODUCTION USE. |
/inotify-cxx/trunk/LICENSE-X11 |
---|
1,22 → 1,25 |
Copyright (c) 2006 Lukas Jelinek |
Copyright (C) 1996 X Consortium |
Permission is hereby granted, free of charge, to any person |
obtaining a copy of this software and associated documentation |
files (the "Software"), to deal in the Software without |
restriction, including without limitation the rights to use, |
copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the |
Software is furnished to do so, subject to the following |
conditions: |
Permission is hereby granted, free of charge, to any person obtaining |
a copy of this software and associated documentation files (the |
"Software"), to deal in the Software without restriction, including |
without limitation the rights to use, copy, modify, merge, publish, |
distribute, sublicense, and/or sell copies of the Software, and to permit |
persons to whom the Software is furnished to do so, subject to the |
following conditions: |
The above copyright notice and this permission notice shall be |
included in all copies or substantial portions of the Software. |
The above copyright notice and this permission notice shall be included |
in all copies or substantial portions of the Software. |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
OTHER DEALINGS IN THE SOFTWARE. |
Except as contained in this notice, the name of the X Consortium shall |
not be used in advertising or otherwise to promote the sale, use or |
other dealings in this Software without prior written authorization from |
the X Consortium. |