// Shared_Memory_Pool.cpp #include "ace/Shared_Memory_Pool.h" #include "ace/OS_NS_sys_shm.h" #include "ace/Log_Category.h" #if !defined (ACE_LACKS_SYSV_SHMEM) ACE_BEGIN_VERSIONED_NAMESPACE_DECL ACE_ALLOC_HOOK_DEFINE(ACE_Shared_Memory_Pool) ACE_Shared_Memory_Pool_Options::ACE_Shared_Memory_Pool_Options ( const char *base_addr, size_t max_segments, size_t file_perms, ACE_OFF_T minimum_bytes, size_t segment_size) : base_addr_ (base_addr), max_segments_ (max_segments), minimum_bytes_ (minimum_bytes), file_perms_ (file_perms), segment_size_ (segment_size) { ACE_TRACE ("ACE_Shared_Memory_Pool_Options::ACE_Shared_Memory_Pool_Options"); } void ACE_Shared_Memory_Pool::dump () const { #if defined (ACE_HAS_DUMP) ACE_TRACE ("ACE_Shared_Memory_Pool::dump"); #endif /* ACE_HAS_DUMP */ } int ACE_Shared_Memory_Pool::in_use (ACE_OFF_T &offset, size_t &counter) { #ifndef ACE_HAS_SYSV_IPC ACE_UNUSED_ARG (offset); ACE_UNUSED_ARG (counter); ACE_NOTSUP_RETURN (-1); #else offset = 0; SHM_TABLE *st = reinterpret_cast (this->base_addr_); shmid_ds buf; for (counter = 0; counter < this->max_segments_ && st[counter].used_ == 1; counter++) { if (ACE_OS::shmctl (st[counter].shmid_, IPC_STAT, &buf) == -1) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("shmctl")), -1); offset += buf.shm_segsz; // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) segment size = %d, offset = %d\n"), buf.shm_segsz, offset)); } return 0; #endif } int ACE_Shared_Memory_Pool::find_seg (const void* const searchPtr, ACE_OFF_T &offset, size_t &counter) { #ifndef ACE_HAS_SYSV_IPC ACE_UNUSED_ARG (searchPtr); ACE_UNUSED_ARG (offset); ACE_UNUSED_ARG (counter); ACE_NOTSUP_RETURN (-1); #else offset = 0; SHM_TABLE *st = reinterpret_cast (this->base_addr_); shmid_ds buf; for (counter = 0; counter < this->max_segments_ && st[counter].used_ == 1; counter++) { if (ACE_OS::shmctl (st[counter].shmid_, IPC_STAT, &buf) == -1) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("shmctl")), -1); offset += buf.shm_segsz; // If segment 'counter' starts at a location greater than the // place we are searching for. We then decrement the offset to // the start of counter-1. (flabar@vais.net) if (((ptrdiff_t) offset + (ptrdiff_t) (this->base_addr_)) > (ptrdiff_t) searchPtr) { --counter; offset -= buf.shm_segsz; return 0; } // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) segment size = %d, offset = %d\n"), buf.shm_segsz, offset)); } return 0; #endif } int ACE_Shared_Memory_Pool::commit_backing_store_name (size_t rounded_bytes, ACE_OFF_T &offset) { ACE_TRACE ("ACE_Shared_Memory_Pool::commit_backing_store_name"); size_t counter; SHM_TABLE *st = reinterpret_cast (this->base_addr_); if (this->in_use (offset, counter) == -1) return -1; if (counter == this->max_segments_) ACELIB_ERROR_RETURN ((LM_ERROR, "exceeded max number of segments = %d, base = %u, offset = %u\n", counter, this->base_addr_, static_cast(offset)), -1); else { int shmid = ACE_OS::shmget (st[counter].key_, rounded_bytes, this->file_perms_ | IPC_CREAT | IPC_EXCL); if (shmid == -1) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("shmget")), -1); st[counter].shmid_ = shmid; st[counter].used_ = 1; void *address = (void *) (((char *) this->base_addr_) + offset); void *shmem = ACE_OS::shmat (st[counter].shmid_, (char *) address, 0); if (shmem != address) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT("(%P|%t) %p, shmem = %u, address = %u\n"), ACE_TEXT("shmat"), shmem, address), -1); } return 0; } /// Handle SIGSEGV and SIGBUS signals to remap shared memory properly. int ACE_Shared_Memory_Pool::handle_signal (int, siginfo_t *siginfo, ucontext_t *) { ACE_TRACE ("ACE_Shared_Memory_Pool::handle_signal"); // While FreeBSD 5.X has a siginfo_t struct with a si_addr field, // it does not define SEGV_MAPERR. #if defined (ACE_HAS_SIGINFO_T) && !defined (ACE_LACKS_SI_ADDR) && \ (defined (SEGV_MAPERR) || defined (SEGV_MEMERR)) if (siginfo == 0) return -1; ACE_OFF_T offset; // Make sure that the pointer causing the problem is within the // range of the backing store. // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) si_signo = %d, si_code = %d, addr = %u\n"), siginfo->si_signo, siginfo->si_code, siginfo->si_addr)); size_t counter = 0; if (this->in_use (offset, counter) == -1) { ACELIB_ERROR ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("in_use"))); } else if (!(siginfo->si_code == SEGV_MAPERR && siginfo->si_addr < (((char *) this->base_addr_) + offset) && siginfo->si_addr >= ((char *) this->base_addr_))) { ACELIB_ERROR_RETURN ((LM_ERROR, "(%P|%t) address %u out of range\n", siginfo->si_addr), -1); } // The above if case will check to see that the address is in the // proper range. Therefore there is a segment out there that the // pointer wants to point into. Find the segment that someone else // has used and attach to it (flabar@vais.net) counter = 0; // ret value to get shmid from the st table. if (this->find_seg (siginfo->si_addr, offset, counter) == -1) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("in_use")), -1); void *address = (void *) (((char *) this->base_addr_) + offset); SHM_TABLE *st = reinterpret_cast (this->base_addr_); void *shmem = ACE_OS::shmat (st[counter].shmid_, (char *) address, 0); if (shmem != address) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT("(%P|%t) %p, shmem = %u, address = %u\n"), ACE_TEXT("shmat"), shmem, address), -1); // NOTE: this won't work if we dont have SIGINFO_T or SI_ADDR #else ACE_UNUSED_ARG (siginfo); #endif /* ACE_HAS_SIGINFO_T && !defined (ACE_LACKS_SI_ADDR) */ return 0; } ACE_Shared_Memory_Pool::ACE_Shared_Memory_Pool ( const ACE_TCHAR *backing_store_name, const OPTIONS *options) : base_addr_ (0), file_perms_ (ACE_DEFAULT_FILE_PERMS), max_segments_ (ACE_DEFAULT_MAX_SEGMENTS), minimum_bytes_ (0), segment_size_ (ACE_DEFAULT_SEGMENT_SIZE) { ACE_TRACE ("ACE_Shared_Memory_Pool::ACE_Shared_Memory_Pool"); // Only change the defaults if != 0. if (options) { this->base_addr_ = reinterpret_cast (const_cast (options->base_addr_)); this->max_segments_ = options->max_segments_; this->file_perms_ = options->file_perms_; this->minimum_bytes_ = options->minimum_bytes_; this->segment_size_ = options->segment_size_; } #ifndef ACE_HAS_SYSV_IPC ACE_UNUSED_ARG (backing_store_name); #else if (backing_store_name) { // Convert the string into a number that is used as the segment // key. int segment_key = 0; #if !defined (ACE_LACKS_SSCANF) int result = ::sscanf (ACE_TEXT_ALWAYS_CHAR (backing_store_name), "%d", &segment_key); #else int result = 0; #endif /* ACE_LACKS_SSCANF */ if (result == 0 || result == EOF) // The conversion to a number failed so hash with crc32 // ACE::crc32 is also used in . this->base_shm_key_ = (key_t) ACE::crc32 (ACE_TEXT_ALWAYS_CHAR (backing_store_name)); else this->base_shm_key_ = segment_key; if (this->base_shm_key_ == IPC_PRIVATE) // Make sure that the segment can be shared between unrelated // processes. this->base_shm_key_ = ACE_DEFAULT_SHM_KEY; } else this->base_shm_key_ = ACE_DEFAULT_SHM_KEY; #endif // ACE_HAS_SYSV_IPC if (this->signal_handler_.register_handler (SIGSEGV, this) == -1) ACELIB_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("ACE_Sig_Handler::register_handler"))); } ACE_Shared_Memory_Pool::~ACE_Shared_Memory_Pool () { } // Ask system for more shared memory. void * ACE_Shared_Memory_Pool::acquire (size_t nbytes, size_t &rounded_bytes) { ACE_TRACE ("ACE_Shared_Memory_Pool::acquire"); rounded_bytes = this->round_up (nbytes); // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) acquiring more chunks, nbytes = %d, rounded_bytes = %d\n"), nbytes, rounded_bytes)); ACE_OFF_T offset; if (this->commit_backing_store_name (rounded_bytes, offset) == -1) return 0; // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P|%t) acquired more chunks, nbytes = %d, rounded_bytes = %d\n"), nbytes, rounded_bytes)); return ((char *) this->base_addr_) + offset; } // Ask system for initial chunk of shared memory. void * ACE_Shared_Memory_Pool::init_acquire (size_t nbytes, size_t &rounded_bytes, int &first_time) { ACE_TRACE ("ACE_Shared_Memory_Pool::init_acquire"); ACE_OFF_T shm_table_offset = ACE::round_to_pagesize (sizeof (SHM_TABLE)); rounded_bytes = this->round_up (nbytes > (size_t) this->minimum_bytes_ ? nbytes : (size_t) this->minimum_bytes_); // Acquire the semaphore to serialize initialization and prevent // race conditions. int shmid = ACE_OS::shmget (this->base_shm_key_, rounded_bytes + shm_table_offset, this->file_perms_ | IPC_CREAT | IPC_EXCL); if (shmid == -1) { if (errno != EEXIST) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("shmget")), 0); first_time = 0; shmid = ACE_OS::shmget (this->base_shm_key_, 0, 0); if (shmid == -1) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("(%P|%t) %p\n"), ACE_TEXT ("shmget")), 0); // This implementation doesn't care if we don't get the key we // want... this->base_addr_ = ACE_OS::shmat (shmid, reinterpret_cast (this->base_addr_), 0); if (this->base_addr_ == reinterpret_cast (-1)) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT("(%P|%t) %p, base_addr = %u\n"), ACE_TEXT("shmat"), this->base_addr_), 0); } else { first_time = 1; // This implementation doesn't care if we don't get the key we // want... this->base_addr_ = ACE_OS::shmat (shmid, reinterpret_cast (this->base_addr_), 0); if (this->base_addr_ == reinterpret_cast (-1)) ACELIB_ERROR_RETURN ((LM_ERROR, ACE_TEXT("(%P|%t) %p, base_addr = %u\n"), ACE_TEXT("shmat"), this->base_addr_), 0); SHM_TABLE *st = reinterpret_cast (this->base_addr_); st[0].key_ = this->base_shm_key_; st[0].shmid_ = shmid; st[0].used_ = 1; for (size_t counter = 1; // Skip over the first entry... counter < this->max_segments_; counter++) { #ifdef ACE_HAS_SYSV_IPC st[counter].key_ = this->base_shm_key_ + counter; #endif st[counter].shmid_ = 0; st[counter].used_ = 0; } } return (void *) (((char *) this->base_addr_) + shm_table_offset); } // Instruct the memory pool to release all of its resources. int ACE_Shared_Memory_Pool::release (int) { ACE_TRACE ("ACE_Shared_Memory_Pool::release"); int result = 0; SHM_TABLE *st = reinterpret_cast (this->base_addr_); for (size_t counter = 0; counter < this->max_segments_ && st[counter].used_ == 1; counter++) if (ACE_OS::shmctl (st[counter].shmid_, IPC_RMID, 0) == -1) result = -1; return result; } int ACE_Shared_Memory_Pool::sync (ssize_t, int) { ACE_TRACE ("ACE_Shared_Memory_Pool::sync"); return 0; } int ACE_Shared_Memory_Pool::sync (void *, size_t, int) { ACE_TRACE ("ACE_Shared_Memory_Pool::sync"); return 0; } int ACE_Shared_Memory_Pool::protect (ssize_t, int) { ACE_TRACE ("ACE_Shared_Memory_Pool::protect"); return 0; } int ACE_Shared_Memory_Pool::protect (void *, size_t, int) { ACE_TRACE ("ACE_Shared_Memory_Pool::protect"); return 0; } void * ACE_Shared_Memory_Pool::base_addr () const { ACE_TRACE ("ACE_Shared_Memory_Pool::base_addr"); return this->base_addr_; } // Implement the algorithm for rounding up the request to an // appropriate chunksize. size_t ACE_Shared_Memory_Pool::round_up (size_t nbytes) { ACE_TRACE ("ACE_Shared_Memory_Pool::round_up"); if (nbytes < this->segment_size_) nbytes = this->segment_size_; return ACE::round_to_pagesize (nbytes); } ACE_END_VERSIONED_NAMESPACE_DECL #endif /* !ACE_LACKS_SYSV_SHMEM */