AJA NTV2 SDK  17.0.1.1246
NTV2 SDK 17.0.1.1246
memory.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
8 #include "ajabase/system/lock.h"
9 #include "ajabase/system/debug.h"
10 #if defined(AJA_LINUX) || defined(AJA_MAC)
11  #include <errno.h>
12  #include <fcntl.h>
13  #include <stdlib.h>
14  #include <syslog.h>
15  #include <sys/mman.h>
16  #include <sys/stat.h>
17  #include <sys/types.h>
18  #include <unistd.h>
19  #include <string.h> // for strerror
20 #elif defined(MSWindows)
21  #include "ajabase/system/system.h" // for Windows API #includes
22 #elif defined(AJA_BAREMETAL)
23  #include <malloc.h>
24 #endif
25 #include <iostream>
26 
27 // structure to track shared memory allocations
28 struct SharedData
29 {
30  std::string shareName;
31  void* pMemory;
32  size_t memorySize;
33  int32_t refCount;
34 #if defined(AJA_WINDOWS)
35  HANDLE fileMapHandle;
36 #else
38 #endif
39 
41  {
42  shareName = "";
43  pMemory = NULL;
44  memorySize = 0;
45  refCount = 0;
46 #if defined(AJA_WINDOWS)
47  fileMapHandle = NULL;
48 #else
49  fileDescriptor = 0;
50 #endif
51  }
52 };
53 
54 // lock for shared memory allocation/free
56 
57 // list of allocated shared memory
58 static std::list<SharedData> sSharedList;
59 
61 {
62 }
63 
64 
66 {
67 }
68 
69 
70 void*
71 AJAMemory::Allocate(size_t memorySize)
72 {
73  if (memorySize == 0)
74  {
75  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::Allocate size is 0");
76  return NULL;
77  }
78 
79  void* pMemory;
80 
81  // allocate memory with no specific alignment
82 #if defined(AJA_WINDOWS)
83  pMemory = malloc(memorySize);
84 #else
85  pMemory = malloc(memorySize);
86 #endif
87 
88  if(pMemory == NULL)
89  {
90  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::Allocate allocation failed");
91  }
92 
93  return pMemory;
94 }
95 
96 
97 void AJAMemory::Free(void* pMemory)
98 {
99  if (pMemory == NULL)
100  {
101  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::Free memory address is NULL");
102  return;
103  }
104 
105  // free memory with no specific alignment
106 #if defined(AJA_WINDOWS)
107  free(pMemory);
108 #else
109  free(pMemory);
110 #endif
111 }
112 
113 
114 void*
115 AJAMemory::AllocateAligned(size_t size, size_t alignment)
116 {
117  if (size == 0)
118  {
119  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::AllocateAligned size is 0");
120  return NULL;
121  }
122 
123  void* pMemory = NULL;
124 
125  // allocate aligned memory
126 #if defined(AJA_WINDOWS)
127  pMemory = _aligned_malloc(size, alignment);
128 #elif defined(AJA_BAREMETAL)
129  pMemory = memalign(alignment, size);
130 #else
131  if (posix_memalign(&pMemory, alignment, size))
132  pMemory = NULL;
133 #endif
134 
135  if(pMemory == NULL)
136  {
137  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::AllocateAligned allocation failed size=%d alignment=%d", (int)size, (int)alignment);
138  }
139 
140  return pMemory;
141 }
142 
143 
144 void
146 {
147  if (pMemory == NULL)
148  {
149  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::FreeAligned memory address is NULL");
150  return;
151  }
152 
153  if (pMemory == NULL)
154  {
155  return;
156  }
157 
158  // free aligned memory
159 #if defined(AJA_WINDOWS)
160  _aligned_free(pMemory);
161 #else
162  free(pMemory);
163 #endif
164 
165 }
166 
167 
168 void*
169 AJAMemory::AllocateShared(size_t* pMemorySize, const char* pShareName, bool global)
170 {
171  AJAAutoLock lock(&sSharedLock);
172 
173  // test parameters
174  if (pMemorySize == NULL)
175  {
176  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::AllocateShared size is NULL");
177  return NULL;
178  }
179 
180  if (*pMemorySize == 0)
181  {
182  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::AllocateShared size is 0");
183  return NULL;
184  }
185 
186  if (pShareName == NULL)
187  {
188  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::AllocateShared share name is NULL");
189  return NULL;
190  }
191 
192  if (*pShareName == '\0')
193  {
194  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::AllocateShared share name is empty");
195  return NULL;
196  }
197 
198  // look for share with the same name
199  size_t sizeInBytes = (*pMemorySize + AJA_PAGE_SIZE - 1) / AJA_PAGE_SIZE * AJA_PAGE_SIZE;
200 
201  std::string name;
202 #if defined(AJA_WINDOWS)
203  if (global)
204  name = "Global\\";
205  name += pShareName;
206 #elif defined(AJA_LINUX)
207  // Docs say to start name with a slash
208  (void) global;
209  name = "/";
210  name += pShareName;
211 #elif defined(AJA_BAREMETAL)
212  // TODO
213  name = pShareName;
214 #else //Mac
215  (void) global;
216  name = pShareName;
217 #endif
218 
219  std::list<SharedData>::iterator shareIter;
220  for (shareIter = sSharedList.begin(); shareIter != sSharedList.end(); ++shareIter)
221  {
222  // if share name match return this share
223  if (name == shareIter->shareName)
224  {
225  // increment shared reference count
226  shareIter->refCount++;
227 
228  // update memory size and return address
229  *pMemorySize = shareIter->memorySize;
230  return shareIter->pMemory;
231  }
232  }
233 
234  SharedData newData;
235 
236  // allocate new share
237 #if defined(AJA_WINDOWS)
238  if (global)
239  {
240  // cross-user access
241  SECURITY_DESCRIPTOR sd;
242  BOOL rv = InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
243  if (rv != FALSE) {
244  // Add the ACL to the security descriptor.
245  rv = SetSecurityDescriptorDacl(&sd,
246  TRUE, // bDaclPresent flag
247  NULL, // this makes it completely open
248  FALSE); // not a default DACL
249  if (rv != FALSE)
250  {
251  SECURITY_ATTRIBUTES sa;
252  sa.nLength = sizeof (SECURITY_ATTRIBUTES);
253  sa.lpSecurityDescriptor = &sd;
254  sa.bInheritHandle = FALSE;
255  newData.fileMapHandle = CreateFileMapping(INVALID_HANDLE_VALUE,
256  &sa, PAGE_READWRITE, 0, (DWORD)sizeInBytes, name.c_str());
257  }
258  else
259  {
260  DWORD err = GetLastError();
262  "AJAMemory::AllocateShared SetSecurityDescriptorDacl failed (err=%d)", err);
263  }
264  }
265  else
266  {
267  DWORD err = GetLastError();
269  "AJAMemory::AllocateShared InitializeSecurityDescriptor failed (err=%d)", err);
270  }
271  }
272  else
273  {
274  // Local user access only
275  newData.fileMapHandle = CreateFileMapping(INVALID_HANDLE_VALUE,
276  NULL, PAGE_READWRITE, 0, (DWORD)sizeInBytes, name.c_str());
277  }
278 
279  if (newData.fileMapHandle == NULL)
280  {
281  DWORD err = GetLastError();
282  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::AllocateShared CreateFileMapping failed (err=%d)", err);
283  return NULL;
284  }
285  newData.pMemory = MapViewOfFile(newData.fileMapHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeInBytes);
286  if (newData.pMemory == NULL)
287  {
288  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::AllocateShared MapViewOfFile failed");
289  return NULL;
290  }
291 
292  // In User Mode: Global\somename
293  // In Kernel Mode: \BaseNamedObjects\somename
294 #elif defined(AJA_BAREMETAL)
295  // TODO
296  return NULL;
297 #else
298  // Mac and Linux
299  {
300  newData.fileDescriptor = shm_open (name.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
301  if (newData.fileDescriptor < 0)
302  {
303  syslog(LOG_ERR, "AJAMemory::AllocateShared -- shm_open failed");
304  return NULL;
305  }
306 
307  bool needsTruncate = false;
308 #if defined(AJA_LINUX)
309  needsTruncate = true;
310  // on Linux shm_open() doesn't set S_IROTH|S_IWOTH, so use fchmod()
311  fchmod (newData.fileDescriptor, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
312 #else
313  // need this on Mac, see:
314  // http://stackoverflow.com/questions/25502229/ftruncate-not-working-on-posix-shared-memory-in-mac-os-x#25510361
315  struct stat mapstat;
316  if (fstat (newData.fileDescriptor, &mapstat) != -1)
317  {
318  if (mapstat.st_size == 0)
319  needsTruncate = true;
320  else if (size_t(mapstat.st_size) < sizeInBytes)
321  {
322  //std::cerr << "AJAMemory::AllocateShared: 'fstat' size=" << mapstat.st_size << " < sizeInBytes=" << sizeInBytes
323  // << " adjusted requested size downward to match existing size" << std::endl;
324  sizeInBytes = mapstat.st_size;
325  }
326  }
327 #endif
328  if (needsTruncate)
329  {
330  // Creation is zero length. This call actually sets the size.
331  // Mac only needs this called the first time created
332  int retVal = ftruncate (newData.fileDescriptor, sizeInBytes);
333  if (retVal)
334  syslog(LOG_ERR, "AJAMemory::AllocateShared -- ftruncate failed\n");
335  }
336 
337  newData.pMemory = mmap (NULL, sizeInBytes, PROT_READ | PROT_WRITE, MAP_SHARED, newData.fileDescriptor, 0);
338  if (newData.pMemory == MAP_FAILED)
339  {
340  std::ostringstream oss; oss << "AJAMemory::AllocateShared: 'mmap' failed, '" << name << "' fd=" << newData.fileDescriptor
341  << " size=" << sizeInBytes << " trunc=" << (needsTruncate?"Y":"N") << " errno=" << errno << " -- " << strerror(errno);
342  syslog(LOG_ERR, "%s\n", oss.str().c_str());
343  // Just because we failed, don't ruin it for others. If we unlink, nobody will
344  // be able to attach to the shared memory ever again.
345  // shm_unlink(name.c_str());
346  return NULL;
347  }
348  }
349 #endif // AJA_LINUX || AJA_MAC
350 
351  // save details
352  newData.shareName = name;
353  newData.memorySize = sizeInBytes;
354  newData.refCount = 1;
355 
356  // add to shared list
357  sSharedList.push_back(newData);
358 
359  // update memory size and return address
360  *pMemorySize = sizeInBytes;
361  return newData.pMemory;
362 }
363 
364 
365 void
366 AJAMemory::FreeShared(void* pMemory)
367 {
368  AJAAutoLock lock(&sSharedLock);
369 
370  // look for memory at the same address
371  std::list<SharedData>::iterator shareIter;
372  for (shareIter = sSharedList.begin(); shareIter != sSharedList.end(); ++shareIter)
373  {
374  if (pMemory == shareIter->pMemory)
375  {
376  shareIter->refCount--;
377  if (shareIter->refCount <= 0)
378  {
379 #if defined(AJA_WINDOWS)
380  UnmapViewOfFile(shareIter->pMemory);
381  CloseHandle(shareIter->fileMapHandle);
382 #elif defined(AJA_BAREMETAL)
383  // TODO
384 #else
385  munmap(shareIter->pMemory, shareIter->memorySize);
386  close(shareIter->fileDescriptor);
387  // This is ugly. If we call shm_unlink, then the shared file name will
388  // be removed from /dev/shm, and future calls to shm_open will create a
389  // different memory share. Will there be a problem with multiple calls
390  // to shm_open with no intervening calls to shm_unlink? Time will tell.
391 // shm_unlink(shareIter->shareName.c_str());
392 #endif
393  sSharedList.erase(shareIter);
394  }
395  return;
396  }
397  }
398 
399  AJA_REPORT(0, AJA_DebugSeverity_Error, "AJAMemory::FreeShared memory not found" /*, pMemory*/);
400 }
SharedData::pMemory
void * pMemory
Definition: memory.cpp:31
HANDLE
short HANDLE
Definition: ajatypes.h:304
SharedData::memorySize
size_t memorySize
Definition: memory.cpp:32
NULL
#define NULL
Definition: ntv2caption608types.h:19
AJA_DebugSeverity_Error
@ AJA_DebugSeverity_Error
Definition: debugshare.h:28
sSharedList
static std::list< SharedData > sSharedList
Definition: memory.cpp:58
SharedData
Definition: memory.cpp:28
AJAMemory::FreeAligned
static void FreeAligned(void *pMemory)
Definition: memory.cpp:145
lock.h
Declares the AJALock class.
AJAMemory::AllocateShared
static void * AllocateShared(size_t *size, const char *pShareName, bool global=true)
Definition: memory.cpp:169
AJAMemory::Allocate
static void * Allocate(size_t size)
Definition: memory.cpp:71
AJA_REPORT
#define AJA_REPORT(_index_, _severity_, _format_,...)
Definition: debug.h:117
AJAMemory::FreeShared
static void FreeShared(void *pMemory)
Definition: memory.cpp:366
AJALock
Definition: lock.h:30
AJAAutoLock
Definition: lock.h:91
system.h
System specific functions.
sSharedLock
static AJALock sSharedLock
Definition: memory.cpp:55
SharedData::fileDescriptor
int fileDescriptor
Definition: memory.cpp:37
SharedData::SharedData
SharedData()
Definition: memory.cpp:40
SharedData::shareName
std::string shareName
Definition: memory.cpp:30
INVALID_HANDLE_VALUE
#define INVALID_HANDLE_VALUE
Definition: ajatypes.h:318
AJAMemory::AllocateAligned
static void * AllocateAligned(size_t size, size_t alignment)
Definition: memory.cpp:115
SharedData::refCount
int32_t refCount
Definition: memory.cpp:33
memory.h
Declares the AJAMemory class.
AJAMemory::AJAMemory
AJAMemory()
Definition: memory.cpp:60
AJAMemory::~AJAMemory
virtual ~AJAMemory()
Definition: memory.cpp:65
debug.h
Declares the AJADebug class.
AJAMemory::Free
static void Free(void *pMemory)
Definition: memory.cpp:97