Browse Source

Version 1.0.0

Rind 8 years ago
parent
commit
db91d7f3f0

BIN
Toolchain/arm/libgfaipc.so.1.0.0


BIN
Toolchain/arm/libgfaipcd.so.1.0.0


BIN
Toolchain/x86_64/libgfaipc.so.1.0.0


BIN
Toolchain/x86_64/libgfaipcd.so.1.0.0


+ 2 - 2
bin/builddbg.sh

@@ -9,8 +9,8 @@ LIBBASENAME="gfaipcd"
 LIBFILENAME="lib$LIBBASENAME.so.1.0.0"
 LINKNAME="lib$LIBBASENAME.so"
 
-$CC $CCOPTS -c ../src/uuid.c ../src/sema.cpp ../src/shm.cpp ../src/shmrot.cpp ../src/ipcshm.cpp
-$CC -g -fPIC -shared -L. -Wl,-soname,$LIBFILENAME -o $LIBFILENAME uuid.o sema.o shm.o shmrot.o ipcshm.o -lc
+$CC $CCOPTS -c ../src/uuid.c ../src/sema.cpp ../src/mutex.cpp ../src/shm.cpp ../src/shmrot.cpp ../src/ipcshm.cpp
+$CC -g -fPIC -shared -L. -Wl,-soname,$LIBFILENAME -o $LIBFILENAME uuid.o sema.o mutex.o shm.o shmrot.o ipcshm.o -lc
 rm -f *.o
 cp $LIBFILENAME ../Toolchain/x86_64
 

+ 2 - 2
bin/buildrel.sh

@@ -9,8 +9,8 @@ LIBBASENAME="gfaipc"
 LIBFILENAME="lib$LIBBASENAME.so.1.0.0"
 LINKNAME="lib$LIBBASENAME.so"
 
-$CC $CCOPTS -c ../src/uuid.c ../src/sema.cpp ../src/shm.cpp ../src/shmrot.cpp ../src/ipcshm.cpp
-$CC -O3 -fPIC -shared -L. -Wl,-soname,$LIBFILENAME -o $LIBFILENAME uuid.o sema.o shm.o shmrot.o ipcshm.o -lc
+$CC $CCOPTS -c ../src/uuid.c ../src/sema.cpp ../src/mutex.cpp ../src/shm.cpp ../src/shmrot.cpp ../src/ipcshm.cpp
+$CC -O3 -fPIC -shared -L. -Wl,-soname,$LIBFILENAME -o $LIBFILENAME uuid.o sema.o mutex.o shm.o shmrot.o ipcshm.o -lc -pthread
 rm -f *.o
 cp $LIBFILENAME ../Toolchain/x86_64
 

+ 2 - 2
dev/builddbg.sh

@@ -10,8 +10,8 @@ LIBBASENAME="gfaipcd"
 LIBFILENAME="lib$LIBBASENAME.so.1.0.0"
 LINKNAME="lib$LIBBASENAME.so"
 
-$CC $CCOPTS -c ../src/uuid.c ../src/sema.cpp ../src/shm.cpp ../src/shmrot.cpp ../src/ipcshm.cpp
-$CC -g -fPIC -shared -L. -Wl,-soname,$LIBFILENAME -o $LIBFILENAME uuid.o sema.o shm.o shmrot.o ipcshm.o -lc
+$CC $CCOPTS -c ../src/uuid.c ../src/sema.cpp ../src/mutex.cpp ../src/shm.cpp ../src/shmrot.cpp ../src/ipcshm.cpp
+$CC -g -fPIC -shared -L. -Wl,-soname,$LIBFILENAME -o $LIBFILENAME uuid.o sema.o mutex.o shm.o shmrot.o ipcshm.o -lc
 rm -f *.o
 cp $LIBFILENAME ../Toolchain/arm
 

+ 2 - 2
dev/buildrel.sh

@@ -10,8 +10,8 @@ LIBBASENAME="gfaipc"
 LIBFILENAME="lib$LIBBASENAME.so.1.0.0"
 LINKNAME="lib$LIBBASENAME.so"
 
-$CC $CCOPTS -c ../src/uuid.c ../src/sema.cpp ../src/shm.cpp ../src/shmrot.cpp ../src/ipcshm.cpp
-$CC -O3 -fPIC -shared -L. -Wl,-soname,$LIBFILENAME -o $LIBFILENAME uuid.o sema.o shm.o shmrot.o ipcshm.o -lc
+$CC $CCOPTS -c ../src/uuid.c ../src/sema.cpp ../src/mutex.cpp ../src/shm.cpp ../src/shmrot.cpp ../src/ipcshm.cpp
+$CC -O3 -fPIC -shared -L. -Wl,-soname,$LIBFILENAME -o $LIBFILENAME uuid.o sema.o mutex.o shm.o shmrot.o ipcshm.o -lc
 rm -f *.o
 cp $LIBFILENAME ../Toolchain/arm
 

BIN
gfaipc.ncb


BIN
gfaipc.suo


+ 8 - 0
gfaipc.vcproj

@@ -182,6 +182,10 @@
 				RelativePath=".\main.c"
 				>
 			</File>
+			<File
+				RelativePath=".\src\mutex.cpp"
+				>
+			</File>
 			<File
 				RelativePath=".\src\sema.cpp"
 				>
@@ -212,6 +216,10 @@
 				RelativePath=".\src\gfaipc.h"
 				>
 			</File>
+			<File
+				RelativePath=".\src\mutex.h"
+				>
+			</File>
 			<File
 				RelativePath=".\src\sema.h"
 				>

+ 2 - 2
main.c

@@ -44,8 +44,8 @@ int main(int argc, char *argv[])
 
 	hShm1 = GfaIpcAcquireSHM(_UUID_TESTSTRUCT1, sizeof(TESTSTRUCT1), 1, "Teststruct 1");
 	GfaIpcDumpSHMROT();
-	hShm2 = GfaIpcAcquireSHM(_UUID_TESTSTRUCT2, sizeof(TESTSTRUCT2), 1, "Teststruct 2");
-	GfaIpcDumpSHMROT();
+	hShm2 = GfaIpcAcquireSHM(_UUID_TESTSTRUCT1, sizeof(TESTSTRUCT1), 1, "Teststruct 1");
+//	GfaIpcDumpSHMROT();
 
 	if(hShm1 && hShm2)
 	{

+ 23 - 52
qttest/main.c

@@ -1,79 +1,50 @@
 #include <stdio.h>
+#include <unistd.h>
 #include <gfaipc.h>
 
 #define _UNUSED(v)				v = v
 
-typedef struct _TESTSTRUCT1
+typedef struct _TESTSTRUCT
 {
     int nCounter1;
     int nCounter2;
-}TESTSTRUCT1, *LPTESTSTRUCT1;
-typedef const TESTSTRUCT1 *LPCTESTSTRUCT1;
+}TESTSTRUCT, *LPTESTSTRUCT;
+typedef const TESTSTRUCT *LPCTESTSTRUCT;
 
-typedef struct _TESTSTRUCT2
-{
-    int nCounter1;
-    int nCounter2;
-}TESTSTRUCT2, *LPTESTSTRUCT2;
-typedef const TESTSTRUCT2 *LPCTESTSTRUCT2;
-
-#define _UUID_TESTSTRUCT1			"8c2e723a-5556-4d90-b5ed-bba8575b6f28"
-#define _UUID_TESTSTRUCT2			"8d961df7-96c4-45cd-8dd7-346227782ac0"
-
-static HSHM g_hShm1 = NULL;
-static HSHM g_hShm2 = NULL;
+#define _UUID_TESTSTRUCT			"8c2e723a-5556-4d90-b5ed-bba8575b6f28"
 
 int main(int argc, char *argv[])
 {
-    LPTESTSTRUCT1 p1 = NULL;
-    LPTESTSTRUCT2 p2 = NULL;
+    static HSHM hShm = NULL;
+    LPTESTSTRUCT p = NULL;
 
     _UNUSED(argc);
     _UNUSED(argv);
 
-    GfaIpcDumpSHMROT();
-
-    g_hShm1 = GfaIpcAcquireSHM(_UUID_TESTSTRUCT1, sizeof(TESTSTRUCT1), 1, "Teststruct 1");
-    GfaIpcDumpSHMROT();
-    g_hShm2 = GfaIpcAcquireSHM(_UUID_TESTSTRUCT2, sizeof(TESTSTRUCT2), 1, "Teststruct 2");
-    GfaIpcDumpSHMROT();
+    hShm = GfaIpcAcquireSHM(_UUID_TESTSTRUCT, sizeof(TESTSTRUCT), 1, "Teststruct");
 
-    if(g_hShm1 && g_hShm2)
+    if(hShm)
     {
-        p1 = (LPTESTSTRUCT1)GfaIpcAcquirePointer(g_hShm1);
-        printf("Get pointer: %p\n", p1);
-        p2 = (LPTESTSTRUCT2)GfaIpcAcquirePointer(g_hShm2);
-        printf("Get pointer: %p\n", p2);
+        p = (LPTESTSTRUCT)GfaIpcAcquirePointer(hShm);
+
+        GfaIpcLockSHM(hShm);
+        printf("In Lock 1!\n");
 
-        printf("p1C1: %d, p1C2: %d\n", p1->nCounter1, p1->nCounter2);
-        printf("p2C1: %d, p2C2: %d\n", p2->nCounter1, p2->nCounter2);
+        GfaIpcLockSHM(hShm);
+        printf("In Lock 2!\n");
 
-        GfaIpcLockSHM(g_hShm1);
-        p1->nCounter1++;
-        p1->nCounter2--;
-        GfaIpcUnlockSHM(g_hShm1);
-        printf("p1C1: %d, p1C2: %d\n", p1->nCounter1, p1->nCounter2);
-        printf("p2C1: %d, p2C2: %d\n", p2->nCounter1, p2->nCounter2);
+        sleep(10);
 
-        GfaIpcLockSHM(g_hShm2);
-        p2->nCounter1--;
-        p2->nCounter2++;
-        GfaIpcUnlockSHM(g_hShm2);
-        printf("p1C1: %d, p1C2: %d\n", p1->nCounter1, p1->nCounter2);
-        printf("p2C1: %d, p2C2: %d\n", p2->nCounter1, p2->nCounter2);
+        GfaIpcUnlockSHM(hShm);
+        printf("Out of Lock 2!\n");
 
-        if(p1)
-        {
-            GfaIpcReleasePointer(g_hShm1, p1);
-        }
+        sleep(2);
 
-        if(p2)
-        {
-            GfaIpcReleasePointer(g_hShm2, p2);
-        }
+        GfaIpcUnlockSHM(hShm);
+        printf("Out of Lock 1!\n");
 
-        GfaIpcReleaseSHM(g_hShm1);
-        GfaIpcReleaseSHM(g_hShm2);
+        GfaIpcReleasePointer(hShm, p);
+        GfaIpcReleaseSHM(hShm);
     }
 
     return 0;

+ 1 - 1
qttest/qttest.pro

@@ -3,7 +3,7 @@ CONFIG += console
 CONFIG -= app_bundle
 CONFIG -= qt
 
-QMAKE_LIBS += -lgfaipc
+QMAKE_LIBS += -lgfaipcd -lpthread
 
 target.path = /root/gfaipc
 INSTALLS += target

+ 2 - 2
qttest/qttest.pro.user

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE QtCreatorProject>
-<!-- Written by QtCreator 4.0.2, 2016-11-24T13:47:38. -->
+<!-- Written by QtCreator 4.0.2, 2017-01-25T12:20:18. -->
 <qtcreator>
  <data>
   <variable>EnvironmentId</variable>
@@ -8,7 +8,7 @@
  </data>
  <data>
   <variable>ProjectExplorer.Project.ActiveTarget</variable>
-  <value type="int">1</value>
+  <value type="int">0</value>
  </data>
  <data>
   <variable>ProjectExplorer.Project.EditorSettings</variable>

+ 199 - 0
src/mutex.cpp

@@ -0,0 +1,199 @@
+#ifdef _WIN32
+#define _CRT_SECURE_NO_WARNINGS
+#define _CRT_NONSTDC_NO_WARNINGS
+#include <windows.h>
+#include <io.h>
+#include "../Win32/w32def.h"
+#endif	//	_WIN32
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#ifdef __linux__
+#include <sys/file.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <linux/limits.h>
+#include <sys/shm.h>
+#endif	//	__linux__
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "defines.h"
+#include "mutex.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _IS_VALID_SHMID(id)				((id) >= 0)
+
+/////////////////////////////////////////////////////////////////////////////
+
+CGlobalMutex::CGlobalMutex(void) :	m_nShmID(_INVALID_ID), m_pMutex(NULL)
+{
+	memset(&m_mutexAttr, 0, sizeof(m_mutexAttr));
+	::pthread_mutexattr_init(&m_mutexAttr);
+	::pthread_mutexattr_setpshared(&m_mutexAttr, PTHREAD_PROCESS_SHARED);
+	::pthread_mutexattr_settype(&m_mutexAttr, PTHREAD_MUTEX_RECURSIVE);
+}
+
+CGlobalMutex::~CGlobalMutex(void)
+{
+	Release();
+}
+
+int CGlobalMutex::Create(const uuid_t &ruuid, const char *pszDir)
+{
+	if(!pszDir)
+		pszDir = "";
+
+	size_t nLenReq = strlen(pszDir) + _UUID_STRING_LEN + 2;
+
+	if(nLenReq > PATH_MAX)
+	{
+		TRACE("CGlobalMutex::Create: directory name too long\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	int nRet = -1;
+	char szUuid[_UUID_STRING_LEN + 1];
+	char szShm[PATH_MAX];
+	_uuid_unparse(&ruuid, szUuid, sizeof(szUuid));
+	sprintf(szShm, "%s/%s.mtx", pszDir, szUuid);
+
+	int nFdShm;
+
+	if((nFdShm = ::open(szShm, O_RDWR | O_CREAT | O_TRUNC, _NUMBER_OF_THE_BEAST)) >= 0)
+	{
+		key_t shmKey;
+
+		if((shmKey = ::ftok(szShm, 'R')) >= 0)
+		{
+			if((m_nShmID = ::shmget(shmKey, sizeof(pthread_mutex_t), IPC_CREAT | _NUMBER_OF_THE_BEAST)) >= 0)
+			{
+				struct shmid_ds sds;
+				memset(&sds, 0, sizeof(sds));
+
+				if(!::shmctl(m_nShmID, IPC_STAT, (struct shmid_ds*)&sds))
+				{
+					m_pMutex = (pthread_mutex_t*)::shmat(m_nShmID, NULL, 0);
+
+					if(!sds.shm_atime)
+					{
+						if(!(nRet = ::pthread_mutex_init(m_pMutex, &m_mutexAttr)))
+						{
+							TRACE("CGlobalMutex::Create: Created new Mutex\n");
+							nRet = 1;
+						}
+						else
+						{
+							TRACE("CGlobalMutex::Create failed! Code: %d\n", nRet);
+							nRet = -1;
+						}
+					}
+					else
+					{
+						TRACE("CGlobalMutex::Create: Opened existing Mutex\n");
+						nRet = 0;
+					}
+				}
+				else
+				{
+					TRACE("CGlobalMutex::Create: IPC_STAT failed - errno: %d!\n", errno);
+				}
+			}
+			else
+			{
+				TRACE("CGlobalMutex::Create: Failed to create SHM on Key: 0x%08X - errno: %d!\n", shmKey, errno);
+			}
+		}
+		else
+		{
+			TRACE("CGlobalMutex::Create: Failed to create shmKey - errno: %d!\n", errno);
+		}
+
+		close(nFdShm);
+	}
+	else
+	{
+		TRACE("CGlobalMutex::Create: Failed to open '%s' - errno: %d!\n", szShm, errno);
+	}
+
+	return nRet;
+}
+	
+long CGlobalMutex::Release(void)
+{
+	struct shmid_ds sds;
+	sds.shm_nattch = (shmatt_t)-1;
+
+	if(_IS_VALID_SHMID(m_nShmID) && m_pMutex)
+	{
+		if(!::shmctl(m_nShmID, IPC_STAT, (struct shmid_ds*)&sds))
+		{
+			if((long)sds.shm_nattch > 1)
+			{
+				::pthread_mutexattr_destroy(&m_mutexAttr);
+				::shmdt(m_pMutex);
+				::shmctl(m_nShmID, IPC_STAT, (struct shmid_ds*)&sds);
+				TRACE("CGlobalMutex::Release: Still attached: %ld!\n", (long)(sds.shm_nattch));
+			}
+			else if((long)sds.shm_nattch == 1)
+			{
+				::pthread_mutex_destroy(m_pMutex);
+				::pthread_mutexattr_destroy(&m_mutexAttr);
+				::shmdt(m_pMutex);
+				::shmctl(m_nShmID, IPC_RMID, NULL);
+				sds.shm_nattch = 0;
+				TRACE("CGlobalMutex::Release: Mutex removed!\n");
+			}
+		}
+		else
+		{
+			sds.shm_nattch = (shmatt_t)-1;
+			TRACE("CGlobalMutex::Release: IPC_STAT failed!\n");
+		}
+
+		m_pMutex = NULL;
+		m_nShmID = _INVALID_ID;
+	}
+
+	return (long)sds.shm_nattch;
+}
+
+bool CGlobalMutex::Lock(void)
+{
+	if(m_pMutex)
+		return !::pthread_mutex_lock(m_pMutex);
+	else
+	{
+		TRACE("CGlobalMutex::Lock: Invalid Mutex: %p!\n", m_pMutex);
+		errno = EINVAL;
+		return false;
+	}
+}
+
+bool CGlobalMutex::TryLock(void)
+{
+	if(m_pMutex)
+		return !::pthread_mutex_trylock(m_pMutex);
+	else
+	{
+		TRACE("CGlobalMutex::Lock: Invalid Mutex: %p!\n", m_pMutex);
+		errno = EINVAL;
+		return false;
+	}
+}
+
+bool CGlobalMutex::Unlock(void)
+{
+	if(m_pMutex)
+		return !::pthread_mutex_unlock(m_pMutex);
+	else
+	{
+		TRACE("CGlobalMutex::Lock: Invalid Mutex: %p!\n", m_pMutex);
+		errno = EINVAL;
+		return false;
+	}
+}

+ 45 - 0
src/mutex.h

@@ -0,0 +1,45 @@
+// mutex.h :
+//
+
+#if !defined(AGD_MUTEX_H__307A751D_EF2D_45D6_BBA8_2EB4B79B548D__INCLUDED_)
+#define AGD_MUTEX_H__307A751D_EF2D_45D6_BBA8_2EB4B79B548D__INCLUDED_
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include "uuid.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// mutex.h - Declarations:
+
+#ifdef _WIN32
+#define _DEF_MUTEX_DIR						"."
+#endif	//	_WIN32
+
+#ifdef __linux__
+#define _DEF_MUTEX_DIR						"/tmp"
+#endif	//	__linux__
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CGlobalMutex
+{
+public:
+	CGlobalMutex(void);
+	virtual ~CGlobalMutex(void);
+
+	int Create(const uuid_t &ruuid, const char *pszDir = _DEF_MUTEX_DIR);
+	long Release(void);
+
+	bool Lock(void);
+	bool TryLock(void);
+	bool Unlock(void);
+
+private:
+	int m_nShmID;
+	pthread_mutexattr_t m_mutexAttr;
+	pthread_mutex_t *m_pMutex;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	!defined(AGD_MUTEX_H__307A751D_EF2D_45D6_BBA8_2EB4B79B548D__INCLUDED_)

+ 2 - 0
src/sema.h

@@ -8,6 +8,8 @@
 #include <stdlib.h>
 #include "uuid.h"
 
+#define _SHM_SEMA_RECURSIVE				1
+
 /////////////////////////////////////////////////////////////////////////////
 // sema.h - Declarations:
 

+ 12 - 28
src/shm.cpp

@@ -32,36 +32,21 @@
 class CShmLocker
 {
 public:
-	CShmLocker(CShmSemaphore &rsema) :	m_bLocked(false),
-										m_rsema(rsema)
+	CShmLocker(CGlobalMutex &rMutex) :	m_bLocked(false),
+										m_rMutex(rMutex)
 	{
-		m_bLocked = m_rsema.Lock();
+		m_bLocked = m_rMutex.Lock();
 	}
 
 	~CShmLocker(void)
-	{
-		Unlock();
-	}
-
-	void Unlock(void)
 	{
 		if(m_bLocked)
-			m_bLocked = !m_rsema.Unlock();
-	}
-
-	void UnlockRelease(void)
-	{
-		if(m_bLocked)
-		{
-			if(m_rsema.Unlock())
-				m_rsema.Release();
-			m_bLocked = false;
-		}
+			m_bLocked = !m_rMutex.Unlock();
 	}
 
 private:
 	bool m_bLocked;
-	CShmSemaphore &m_rsema;
+	CGlobalMutex &m_rMutex;
 };
 
 /////////////////////////////////////////////////////////////////////////////
@@ -96,11 +81,11 @@ int CShm::Create(const uuid_t &ruuid, size_t nCbShm, const char *pszDir)
 	_uuid_unparse(&ruuid, szUuid, sizeof(szUuid));
 	sprintf(szShm, "%s/%s.shm", pszDir, szUuid);
 
-	if(m_sema.Create(ruuid, pszDir) < 0)
+	if(m_mutex.Create(ruuid, pszDir) < 0)
 		return -1;
 
 	int nFdShm;
-	CShmLocker locker(m_sema);
+	CShmLocker locker(m_mutex);
 
 	if((nFdShm = ::open(szShm, O_RDWR | O_CREAT | O_TRUNC, _NUMBER_OF_THE_BEAST)) >= 0)
 	{
@@ -158,7 +143,7 @@ int CShm::Create(const uuid_t &ruuid, size_t nCbShm, const char *pszDir)
 long CShm::Release(void)
 {
 	long att = -1;
-	CShmLocker locker(m_sema);
+	CShmLocker locker(m_mutex);
 
 	if(_IS_VALID_SHMID(m_nShmID))
 	{
@@ -177,13 +162,12 @@ long CShm::Release(void)
 		m_nShmID = _INVALID_ID;
 	}
 
-	locker.UnlockRelease();
 	return att;
 }
 
 void* CShm::Attach(const void *pAddr, int shmflg)
 {
-	CShmLocker locker(m_sema);
+	CShmLocker locker(m_mutex);
 
 	if(!_IS_VALID_SHMID(m_nShmID))
 	{
@@ -212,7 +196,7 @@ void CShm::Detach(const void *pAddr)
 	if(pAddr)
 	{
 		TRACE("CShm::Detach: %p!\n", pAddr);
-		CShmLocker locker(m_sema);
+		CShmLocker locker(m_mutex);
 		::shmdt(pAddr);
 	}
 }
@@ -289,12 +273,12 @@ int CShm::ShmInfo(struct shm_info *pinfo)
 
 int CShm::Lock(void)
 {
-	return m_sema.Lock();
+	return m_mutex.Lock();
 }
 
 int CShm::Unlock(void)
 {
-	return m_sema.Unlock();
+	return m_mutex.Unlock();
 }
 
 /////////////////////////////////////////////////////////////////////////////

+ 3 - 1
src/shm.h

@@ -4,6 +4,7 @@
 #if !defined(AGD_SHM_H__AD9FF941_0114_4585_8EF1_0A4ACA7DC0AD__INCLUDED_)
 #define AGD_SHM_H__AD9FF941_0114_4585_8EF1_0A4ACA7DC0AD__INCLUDED_
 
+#include "mutex.h"
 #include "sema.h"
 
 /////////////////////////////////////////////////////////////////////////////
@@ -58,7 +59,8 @@ private:
 
 private:
 	int m_nShmID;
-	CShmSemaphore m_sema;
+//	CShmSemaphore m_sema;
+	CGlobalMutex m_mutex;
 	uuid_t m_uuid;
 };
 

+ 1 - 1
src/shmrot.cpp

@@ -225,7 +225,7 @@ void CShmROT::ReleaseShm(CShm *pShm)
 		}
 
 		delete pShm;
-	}
+        }
 }
 
 int CShmROT::Lock(void)