shared-memory.hpp 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. #pragma once
  2. #include <semaphore.h>
  3. #include <sys/mman.h>
  4. namespace nall {
  5. struct shared_memory {
  6. shared_memory() = default;
  7. shared_memory(const shared_memory&) = delete;
  8. auto operator=(const shared_memory&) -> shared_memory& = delete;
  9. ~shared_memory() {
  10. reset();
  11. }
  12. explicit operator bool() const {
  13. return _mode != mode::inactive;
  14. }
  15. auto size() const -> uint {
  16. return _size;
  17. }
  18. auto acquired() const -> bool {
  19. return _acquired;
  20. }
  21. auto acquire() -> uint8_t* {
  22. if(!acquired()) {
  23. sem_wait(_semaphore);
  24. _acquired = true;
  25. }
  26. return _data;
  27. }
  28. auto release() -> void {
  29. if(acquired()) {
  30. sem_post(_semaphore);
  31. _acquired = false;
  32. }
  33. }
  34. auto reset() -> void {
  35. release();
  36. if(_mode == mode::server) return remove();
  37. if(_mode == mode::client) return close();
  38. }
  39. auto create(const string& name, uint size) -> bool {
  40. reset();
  41. _name = {"/nall-", string{name}.transform("/:", "--")};
  42. _size = size;
  43. //O_CREAT | O_EXCL seems to throw ENOENT even when semaphore does not exist ...
  44. _semaphore = sem_open(_name, O_CREAT, 0644, 1);
  45. if(_semaphore == SEM_FAILED) return remove(), false;
  46. _descriptor = shm_open(_name, O_CREAT | O_TRUNC | O_RDWR, 0644);
  47. if(_descriptor < 0) return remove(), false;
  48. if(ftruncate(_descriptor, _size) != 0) return remove(), false;
  49. _data = (uint8_t*)mmap(nullptr, _size, PROT_READ | PROT_WRITE, MAP_SHARED, _descriptor, 0);
  50. if(_data == MAP_FAILED) return remove(), false;
  51. memory::fill(_data, _size);
  52. _mode = mode::server;
  53. return true;
  54. }
  55. auto remove() -> void {
  56. if(_data) {
  57. munmap(_data, _size);
  58. _data = nullptr;
  59. }
  60. if(_descriptor) {
  61. ::close(_descriptor);
  62. shm_unlink(_name);
  63. _descriptor = -1;
  64. }
  65. if(_semaphore) {
  66. sem_close(_semaphore);
  67. sem_unlink(_name);
  68. _semaphore = nullptr;
  69. }
  70. _mode = mode::inactive;
  71. _name = "";
  72. _size = 0;
  73. }
  74. auto open(const string& name, uint size) -> bool {
  75. reset();
  76. _name = {"/nall-", string{name}.transform("/:", "--")};
  77. _size = size;
  78. _semaphore = sem_open(_name, 0, 0644);
  79. if(_semaphore == SEM_FAILED) return close(), false;
  80. _descriptor = shm_open(_name, O_RDWR, 0644);
  81. if(_descriptor < 0) return close(), false;
  82. _data = (uint8_t*)mmap(nullptr, _size, PROT_READ | PROT_WRITE, MAP_SHARED, _descriptor, 0);
  83. if(_data == MAP_FAILED) return close(), false;
  84. _mode = mode::client;
  85. return true;
  86. }
  87. auto close() -> void {
  88. if(_data) {
  89. munmap(_data, _size);
  90. _data = nullptr;
  91. }
  92. if(_descriptor) {
  93. ::close(_descriptor);
  94. _descriptor = -1;
  95. }
  96. if(_semaphore) {
  97. sem_close(_semaphore);
  98. _semaphore = nullptr;
  99. }
  100. _mode = mode::inactive;
  101. _name = "";
  102. _size = 0;
  103. }
  104. private:
  105. enum class mode : uint { server, client, inactive } _mode = mode::inactive;
  106. string _name;
  107. sem_t* _semaphore = nullptr;
  108. int _descriptor = -1;
  109. uint8_t* _data = nullptr;
  110. uint _size = 0;
  111. bool _acquired = false;
  112. };
  113. }