Merge branch 'dev' of feistymeow.org:feisty_meow into dev
[feisty_meow.git] / nucleus / library / application / shared_memory.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : shared_memory                                                     *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2002-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "shared_memory.h"
16
17 #include <application/windoze_helper.h>
18 #include <basis/astring.h>
19 #include <basis/byte_array.h>
20 #include <basis/utf_conversion.h>
21 #include <basis/functions.h>
22 #include <basis/guards.h>
23 #include <configuration/application_configuration.h>
24 #include <loggers/critical_events.h>
25 #include <filesystem/byte_filer.h>
26 #include <filesystem/filename.h>
27 #include <structures/string_hasher.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #ifdef __UNIX__
32   #include <fcntl.h>
33   #include <sys/ipc.h>
34   #include <sys/mman.h>
35   #include <sys/shm.h>
36   #include <sys/types.h>
37   #include <unistd.h>
38 #endif
39
40 using namespace basis;
41 using namespace configuration;
42 using namespace filesystem;
43 using namespace loggers;
44 using namespace processes;
45 using namespace structures;
46
47 namespace application {
48
49 shared_memory::shared_memory(int size, const char *identity)
50 : _locking(new rendezvous(identity)),
51   _the_memory(NULL_POINTER),
52   _valid(false),
53   _identity(new astring(identity)),
54   _size(size)
55 {
56   FUNCDEF("constructor");
57   bool first_use = false;  // assume already existing until told otherwise.
58   _locking->lock();  // force all others to wait on our finishing creation.
59 #ifdef __UNIX__
60   int flag = O_RDWR | O_CREAT | O_EXCL;
61     // try to create the object if it doesn't exist yet, but fail if it does.
62   int mode = 0700;  // rwx------ for just us.
63   _the_memory = shm_open(special_filename(identity).s(), flag, mode);
64     // create the shared memory object but fail if it already exists.
65   if (negative(_the_memory)) {
66     // failed to create the shared segment.  try without forcing exclusivity.
67     flag = O_RDWR | O_CREAT;
68     _the_memory = shm_open(special_filename(identity).s(), flag, mode);
69     basis::un_int err = critical_events::system_error();  // get last error.
70     if (negative(_the_memory)) {
71       // definitely a failure now...
72       printf("error allocating shared segment for %s, was told %s.\n",
73           special_filename(identity).s(), critical_events::system_error_text(err).s());
74       _the_memory = 0;
75       _locking->unlock();  // release lock before return.
76       return;
77     }
78     // getting to here means the memory was already allocated.  so we're fine.
79   } else {
80     // the shared memory segment was just created this time.
81     int ret = ftruncate(_the_memory, size);
82     basis::un_int err = critical_events::system_error();  // get last error.
83     if (ret) {
84       printf("error truncating shared segment for %s, was told %s.",
85           special_filename(identity).s(), critical_events::system_error_text(err).s());
86     }
87     first_use = true;
88   }
89   _valid = true;
90 #elif defined(__WIN32__)
91   _the_memory = ::CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE,
92       0, size, to_unicode_temp(identity));
93   basis::un_int err = critical_events::system_error();  // get last error.
94   first_use = (err != ERROR_ALREADY_EXISTS);
95   if (!_the_memory) {
96     _locking->unlock();
97     return;  // not healthy.
98   }
99   _valid = true;
100 #else
101 //this is junk; simulates shared memory poorly.
102   #pragma message("simulating shared memory since unknown for this platform.")
103   if (!_bogus_shared_space().length()) {
104     _bogus_shared_space().reset(size);
105     first_use = true;
106   }
107   _the_memory = _bogus_shared_space().access();
108   _valid = true;
109 #endif
110   if (first_use) {
111     // initialize the new memory to all zeros.
112     abyte *contents = locked_grab_memory();
113     if (!contents) {
114       _valid = false;
115        _locking->unlock();
116        return;
117     }
118     memset(contents, 0, size);
119     locked_release_memory(contents);  // release the memory for now.
120   }
121   _locking->unlock();
122 }
123
124 shared_memory::~shared_memory()
125 {
126 #ifdef __UNIX__
127   if (_the_memory) {
128     close(int(_the_memory));
129     shm_unlink(special_filename(identity()).s());
130   }
131 #elif defined(__WIN32__)
132   ::CloseHandle(_the_memory);
133 #else
134   //hmmm: fix.
135   _the_memory = NULL_POINTER;
136 #endif
137   WHACK(_locking);
138   WHACK(_identity);
139   _valid = false;
140 }
141
142 const astring &shared_memory::identity() const { return *_identity; }
143
144 astring shared_memory::special_filename(const astring &identity)
145 {
146   astring shared_file = identity;
147   filename::detooth_filename(shared_file);
148   shared_file = astring("/tmp_") + "sharmem_" + shared_file;
149   return shared_file;
150 }
151
152 astring shared_memory::unique_shared_mem_identifier(int sequencer)
153 {
154   astring to_return("SMID-");
155   to_return += a_sprintf("%d-%d-", application_configuration::process_id(), sequencer);
156   to_return += application_configuration::application_name();
157   // replace file delimiters in the name with a safer character.
158   filename::detooth_filename(to_return);
159   return to_return;
160 }
161
162 bool shared_memory::first_usage(abyte *contents, int max_compare)
163 {
164   for (int i = 0; i < max_compare; i++)
165     if (contents[i] != 0) return false;
166   return true;
167 }
168
169 abyte *shared_memory::lock()
170 {
171   _locking->lock();
172   return locked_grab_memory();
173 }
174
175 void shared_memory::unlock(abyte * &to_unlock)
176 {
177   locked_release_memory(to_unlock);
178   _locking->unlock();
179 }
180
181 abyte *shared_memory::locked_grab_memory()
182 {
183   FUNCDEF("locked_grab_memory")
184   abyte *to_return = NULL_POINTER;
185   if (!_the_memory) return to_return;
186 #ifdef __UNIX__
187   to_return = (abyte *)mmap(NULL_POINTER, _size, PROT_READ | PROT_WRITE,
188       MAP_SHARED, int(_the_memory), 0);
189 #elif defined(__WIN32__)
190   to_return = (abyte *)::MapViewOfFile((HANDLE)_the_memory, FILE_MAP_ALL_ACCESS,
191       0, 0, 0);
192 #else
193   to_return = (abyte *)_the_memory;
194 #endif
195   if (!to_return) {
196 //    FUNCTION(func);
197 //not working yet.  callstack tracker or whatever is hosed up.
198     throw(astring(astring(class_name()) + "::" + func + ": no data was accessible in shared space."));
199   }
200   return to_return;
201 }
202
203 void shared_memory::locked_release_memory(abyte * &to_unlock)
204 {
205   if (!_the_memory || !to_unlock) return;
206 #ifdef __UNIX__
207   munmap(to_unlock, _size);
208 #elif defined(__WIN32__)
209   ::UnmapViewOfFile(to_unlock);
210 #else
211 //uhh.
212 #endif
213 }
214
215 } //namespace.
216