Pārlūkot izejas kodu

INotify Wrapper hinzugefügt

Rind 3 gadi atpakaļ
vecāks
revīzija
e733920527
4 mainītis faili ar 374 papildinājumiem un 3 dzēšanām
  1. 9 3
      gfasitarautils.pro
  2. 290 0
      src/gfainotify.cpp
  3. 74 0
      src/gfainotify.h
  4. 1 0
      src/gfasitarautils.h

+ 9 - 3
gfasitarautils.pro

@@ -6,11 +6,13 @@ CONFIG += c++11 shared thread
 HEADERS += \
     src/gfasitarautils.h \
     src/suconv.h \
-    src/sucycletimer.h
+    src/sucycletimer.h \
+    src/gfainotify.h
 
 SOURCES += \
     src/suconv.c \
-    src/sucycletimer.cpp
+    src/sucycletimer.cpp \
+    src/gfainotify.cpp
 
 ####################################################################################
 
@@ -50,9 +52,11 @@ linux-g++ {
 	includes.extra += -$(INSTALL_FILE) $$PWD/src/gfasitarautils.h $(INSTALL_ROOT)$$includes.path
 	includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/suconv.h $(INSTALL_ROOT)$$includes.path
 	includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/sucycletimer.h $(INSTALL_ROOT)$$includes.path
+	includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/gfainotify.h $(INSTALL_ROOT)$$includes.path
 	includes.uninstall += -$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/gfasitarautils.h
 	includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/suconv.h
 	includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/sucycletimer.h
+	includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/gfainotify.h
 	INSTALLS += includes
 }
 
@@ -75,9 +79,11 @@ linux-buildroot-g++ {
 		includes.extra += -$(INSTALL_FILE) $$PWD/src/gfasitarautils.h $(INSTALL_ROOT)$$includes.path
 		includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/suconv.h $(INSTALL_ROOT)$$includes.path
 		includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/sucycletimer.h $(INSTALL_ROOT)$$includes.path
+		includes.extra += $$escape_expand(\\n\\t)-$(INSTALL_FILE) $$PWD/src/gfainotify.h $(INSTALL_ROOT)$$includes.path
 		includes.uninstall += -$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/gfasitarautils.h
 		includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/suconv.h
 		includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/sucycletimer.h
+		includes.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/gfainotify.h
 		INSTALLS += includes
 
 		itoolchain.target = install_toolchain
@@ -98,6 +104,6 @@ deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET="$(TARGET)" >> deployt
 deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET0="$(TARGET0)" >> deploytargets
 deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET1="$(TARGET1)" >> deploytargets
 deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET2="$(TARGET2)" >> deploytargets
-deploylib.commands += $$escape_expand(\\n\\t)@echo HEADERS="\\\"gfasitarautils.h suconv.h sucycletimer.h\\\"" >> deploytargets
+deploylib.commands += $$escape_expand(\\n\\t)@echo HEADERS="\\\"gfasitarautils.h suconv.h sucycletimer.h gfainotify.h\\\"" >> deploytargets
 PRE_TARGETDEPS += deploylib
 QMAKE_EXTRA_TARGETS += deploylib

+ 290 - 0
src/gfainotify.cpp

@@ -0,0 +1,290 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <poll.h>
+#include <signal.h>
+#include <errno.h>
+#include <limits.h>
+#include "gfainotify.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _THREAD_CMD_WAIT_COND		0
+#define _THREAD_CMD_EXIT			1
+
+/////////////////////////////////////////////////////////////////////////////
+
+CInotify::CInotify(void) :			m_fdIn(-1),
+									m_tCond(PTHREAD_COND_INITIALIZER),
+									m_mtx(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP),
+									m_fdReadPipe(m_fdPipe[0]),
+									m_fdWritePipe(m_fdPipe[1]),
+									m_bThreadRunning(false)
+{
+	m_fdReadPipe = m_fdWritePipe = -1;
+}
+
+CInotify::~CInotify(void)
+{
+	Close();
+	::pthread_cond_destroy(&m_tCond);
+	::pthread_mutex_destroy(&m_mtx);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void CInotify::LockMutex(void)
+{
+	::pthread_mutex_lock(&m_mtx);
+}
+
+void CInotify::UnlockMutex(void)
+{
+	::pthread_mutex_unlock(&m_mtx);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool CInotify::Init(void)
+{
+	bool bRet = true;
+
+	if(m_fdIn >= 0)
+		return true;
+
+	if(m_fdIn == -1)
+	{
+		LockMutex();
+		if(::pipe(m_fdPipe) >= 0)
+		{
+			if((m_fdIn = ::inotify_init1(IN_NONBLOCK)) >= 0)
+			{
+				m_thread.Create(&CInotify::WatchThreadRoutine, reinterpret_cast<void*>(this));
+			}
+		}
+		bRet = (m_fdIn >= 0);
+		UnlockMutex();
+
+		while(!m_bThreadRunning)
+			::pthread_yield();
+	}
+	return bRet;
+}
+
+void CInotify::Close(void)
+{
+	if(!SuspendMonitorThread(_THREAD_CMD_EXIT))
+	{
+		errno = EPERM;
+		return;
+	}
+
+	LockMutex();
+	if(m_fdIn >= 0)
+	{
+		::close(m_fdIn);
+		m_fdIn = -1;
+	}
+	m_thread.Join(NULL);
+	if(m_fdReadPipe >= 0)
+	{
+		close(m_fdReadPipe);
+		m_fdReadPipe = -1;
+	}
+	if(m_fdWritePipe >= 0)
+	{
+		close(m_fdWritePipe);
+		m_fdWritePipe = -1;
+	}
+	UnlockMutex();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool CInotify::SuspendMonitorThread(int sig)
+{
+	if(m_thread.GetID() == ::pthread_self())
+		return true;
+	if(m_fdWritePipe >= 0)
+		return write(m_fdWritePipe, &sig, sizeof(sig)) == sizeof(sig);
+	return false;
+}
+
+void CInotify::ResumeMonitorThread(void)
+{
+	if(m_thread.GetID() != ::pthread_self())
+	{
+		if(m_fdIn >= 0 && m_fdReadPipe >= 0)
+		{
+			LockMutex();
+			::pthread_cond_signal(&m_tCond);
+			UnlockMutex();
+		}
+	}
+}
+
+bool CInotify::SignalMonitorThread(int sig)
+{
+	return write(m_fdWritePipe, &sig, sizeof(sig)) == sizeof(sig);
+}
+
+void CInotify::WaitCondition(void)
+{
+	::pthread_cond_wait(&m_tCond, &m_mtx);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int CInotify::AddWatch(const char *pszFilepath, uint32_t mask, PFN_WATCH_EVENT_CALLBACK pfnCb, void *pParam)
+{
+	int wd;
+	if(!pszFilepath || !*pszFilepath || !pfnCb)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	if(!SuspendMonitorThread(_THREAD_CMD_WAIT_COND))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	LockMutex();
+	if((wd = ::inotify_add_watch(m_fdIn, pszFilepath, mask)) >= 0)
+	{
+		INOTIFY_WATCH iw;
+		iw.wd		= wd;
+		iw.mask		= mask;
+		iw.pfnCb	= pfnCb;
+		iw.pParam	= pParam;
+		m_vWatches.push_back(iw);
+	}
+	UnlockMutex();
+	ResumeMonitorThread();
+	return wd;
+}
+
+int CInotify::RemoveWatch(int wd)
+{
+	int ret;
+
+	if(!SuspendMonitorThread(_THREAD_CMD_WAIT_COND))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	LockMutex();
+	ret = ::inotify_rm_watch(m_fdIn, wd);
+	RemoveWatchByDescriptor(wd);
+	UnlockMutex();
+	ResumeMonitorThread();
+	return ret;
+}
+
+void CInotify::RemoveWatchByDescriptor(int wd)
+{
+	for(auto i = m_vWatches.begin(); i != m_vWatches.end(); ++i)
+	{
+		const INOTIFY_WATCH &iw = *i;
+		if(iw.wd == wd)
+		{
+			m_vWatches.erase(i);
+			return;
+		}
+	}
+}
+
+LPCINOTIFY_WATCH CInotify::FindByWatchDescriptor(int wd) const
+{
+	for(auto i = m_vWatches.begin(); i != m_vWatches.end(); ++i)
+	{
+		const INOTIFY_WATCH &iw = *i;
+		if(iw.wd == wd)
+			return &iw;
+	}
+	return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void* CInotify::WatchThreadRoutine(void *pParam)
+{
+	int ret;
+	bool bRun = true;
+	struct pollfd pfd[2];
+	CInotify *pThis = reinterpret_cast<CInotify*>(pParam);
+	char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
+
+	memset(pfd, 0, sizeof(pfd));
+	pfd[0].events = POLLIN;
+	pfd[1].events = POLLIN;
+
+	pThis->LockMutex();
+	pThis->m_bThreadRunning = true;
+
+	do
+	{
+		if((pThis->m_fdIn >= 0) && (pThis->m_fdReadPipe >= 0))
+		{
+			pfd[0].fd = pThis->m_fdReadPipe;
+			pfd[1].fd = pThis->m_fdIn;
+
+			if((ret = poll(pfd, 2, -1)) > 0)
+			{
+				if(pfd[1].revents & POLLIN)
+				{
+					do
+					{
+						ret = read(pThis->m_fdIn, buf, sizeof(buf));
+						
+						if(ret <= 0)
+						{
+							if(errno != EAGAIN)
+								bRun = false;
+							break;
+						}
+
+						LPCINOTIFY_WATCH pnw;
+						const struct inotify_event *pEvt = NULL;
+
+						for(char *ptr = buf; ptr < (buf + ret); ptr += (sizeof(struct inotify_event) + pEvt->len))
+						{
+							pEvt = (const struct inotify_event*)ptr;
+							if((pnw = pThis->FindByWatchDescriptor(pEvt->wd)))
+							{
+								pThis->UnlockMutex();
+								(*pnw->pfnCb)(*pEvt, pnw->pParam);
+								pThis->LockMutex();
+							}
+						}
+					}
+					while(ret > 0);
+				}
+
+				if(pfd[0].revents & POLLIN)
+				{
+					int cmd;
+					if((ret = read(pThis->m_fdReadPipe, &cmd, sizeof(cmd))) == sizeof(cmd))
+					{
+						switch(cmd)
+						{
+						case _THREAD_CMD_WAIT_COND:
+							pThis->WaitCondition();
+							break;
+						case _THREAD_CMD_EXIT:
+							bRun = false;
+							break;
+						}
+					}
+				}
+			}
+		}
+	}
+	while(bRun);
+
+	pThis->m_bThreadRunning = false;
+	pThis->UnlockMutex();
+	return NULL;
+}

+ 74 - 0
src/gfainotify.h

@@ -0,0 +1,74 @@
+// gfainotify.h :
+//
+
+#if !defined(AGD_GFAINOTIFY_H__27F71F8A_86EC_4CE3_9B76_312D533268AF__INCLUDED_)
+#define AGD_GFAINOTIFY_H__27F71F8A_86EC_4CE3_9B76_312D533268AF__INCLUDED_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/inotify.h>
+
+/////////////////////////////////////////////////////////////////////////////
+#ifdef __cplusplus
+/////////////////////////////////////////////////////////////////////////////
+
+#include <atomic>
+#include <string>
+#include <vector>
+#include <gfa/thread.h>
+
+/////////////////////////////////////////////////////////////////////////////
+// gfainotify.h - Declarations:
+
+typedef void (*PFN_WATCH_EVENT_CALLBACK)(const struct inotify_event& /*evt*/, void* /*pParam*/);
+
+typedef struct _INOTIFY_WATCH
+{
+	int wd;
+	uint32_t mask;
+	PFN_WATCH_EVENT_CALLBACK pfnCb;
+	void *pParam;
+}INOTIFY_WATCH, *LPINOTIFY_WATCH;
+typedef const INOTIFY_WATCH *LPCINOTIFY_WATCH;
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CInotify
+{
+public:
+	CInotify(void);
+	~CInotify(void);
+
+	bool Init(void);
+	void Close(void);
+
+	int AddWatch(const char *pszFilepath, uint32_t mask, PFN_WATCH_EVENT_CALLBACK pfnCb, void *pParam);
+	int RemoveWatch(int wd);
+
+private:
+	static void* WatchThreadRoutine(void *pParam);
+	void LockMutex(void);
+	void UnlockMutex(void);
+	void RemoveWatchByDescriptor(int wd);
+	bool SuspendMonitorThread(int sig);
+	void ResumeMonitorThread(void);
+	bool SignalMonitorThread(int sig);
+	void WaitCondition(void);
+	LPCINOTIFY_WATCH FindByWatchDescriptor(int wd) const;
+
+private:
+	std::atomic_int m_fdIn;
+	CThread m_thread;
+	pthread_cond_t m_tCond;
+	pthread_mutex_t m_mtx;
+	std::vector<INOTIFY_WATCH> m_vWatches;
+	int m_fdPipe[2];
+	int &m_fdReadPipe;
+	int &m_fdWritePipe;
+	std::atomic_bool m_bThreadRunning;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	__cplusplus
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	!defined(AGD_GFAINOTIFY_H__27F71F8A_86EC_4CE3_9B76_312D533268AF__INCLUDED_)

+ 1 - 0
src/gfasitarautils.h

@@ -6,6 +6,7 @@
 
 #include "suconv.h"
 #include "sucycletimer.h"
+#include "gfainotify.h"
 
 /////////////////////////////////////////////////////////////////////////////
 // gfasitarautils.h