Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / library / settingvar.cpp
blob254e39c0e38f732ecde6b9c9a3a008faf88b6588
1 #include "settingvar.hpp"
2 #include "stateobject.hpp"
4 namespace settingvar
6 namespace
8 threads::rlock* global_lock;
10 struct set_internal
12 std::map<std::string, superbase*> supervars;
13 std::set<set::listener*> callbacks;
16 struct group_internal
18 std::map<std::string, class base*> settings;
19 std::set<struct listener*> listeners;
20 std::set<struct set*> sets_listened;
23 typedef stateobject::type<set, set_internal> set_internal_t;
24 typedef stateobject::type<group, group_internal> group_internal_t;
27 threads::rlock& get_setting_lock()
29 if(!global_lock) global_lock = new threads::rlock;
30 return *global_lock;
33 listener::~listener() throw()
37 set::listener::~listener()
41 group::group() throw(std::bad_alloc)
42 : _listener(*this)
46 group::~group() throw()
48 threads::arlock h(get_setting_lock());
49 auto state = group_internal_t::get_soft(this);
50 if(!state) return;
51 for(auto i : state->settings)
52 i.second->group_died();
53 for(auto i : state->sets_listened)
54 i->drop_callback(_listener);
55 group_internal_t::clear(this);
58 std::set<std::string> group::get_settings_set() throw(std::bad_alloc)
60 threads::arlock h(get_setting_lock());
61 auto state = group_internal_t::get_soft(this);
62 std::set<std::string> x;
63 if(state)
64 for(auto i : state->settings)
65 x.insert(i.first);
66 return x;
69 base& group::operator[](const std::string& name)
71 threads::arlock h(get_setting_lock());
72 auto state = group_internal_t::get_soft(this);
73 if(!state || !state->settings.count(name))
74 throw std::runtime_error("No such setting");
75 return *state->settings[name];
78 void group::fire_listener(base& var) throw()
80 std::set<listener*> l;
82 threads::arlock h(get_setting_lock());
83 auto state = group_internal_t::get_soft(this);
84 if(!state) return;
85 for(auto i : state->listeners)
86 l.insert(i);
88 for(auto i : l)
89 try {
90 i->on_setting_change(*this, var);
91 } catch(...) {
95 void group::add_listener(struct listener& listener) throw(std::bad_alloc)
97 threads::arlock h(get_setting_lock());
98 auto& state = group_internal_t::get(this);
99 state.listeners.insert(&listener);
102 void group::remove_listener(struct listener& listener) throw(std::bad_alloc)
104 threads::arlock h(get_setting_lock());
105 auto state = group_internal_t::get_soft(this);
106 if(state)
107 state->listeners.erase(&listener);
110 void group::do_register(const std::string& name, base& _setting) throw(std::bad_alloc)
112 threads::arlock h(get_setting_lock());
113 auto& state = group_internal_t::get(this);
114 if(state.settings.count(name)) return;
115 state.settings[name] = &_setting;
118 void group::do_unregister(const std::string& name, base& _setting) throw(std::bad_alloc)
120 threads::arlock h(get_setting_lock());
121 auto state = group_internal_t::get_soft(this);
122 if(!state || !state->settings.count(name) || state->settings[name] != &_setting) return;
123 state->settings.erase(name);
126 void group::add_set(set& s) throw(std::bad_alloc)
128 threads::arlock u(get_setting_lock());
129 auto& state = group_internal_t::get(this);
130 if(state.sets_listened.count(&s)) return;
131 try {
132 state.sets_listened.insert(&s);
133 s.add_callback(_listener);
134 } catch(...) {
135 state.sets_listened.erase(&s);
139 void group::drop_set(set& s)
141 threads::arlock h(get_setting_lock());
142 auto state = group_internal_t::get_soft(this);
143 if(!state) return;
144 //Drop the callback. This unregisters all.
145 s.drop_callback(_listener);
146 state->sets_listened.erase(&s);
149 group::xlistener::xlistener(group& _grp)
150 : grp(_grp)
154 group::xlistener::~xlistener()
158 void group::xlistener::create(set& s, const std::string& name, superbase& sb)
160 threads::arlock h(get_setting_lock());
161 sb.make(grp);
164 void group::xlistener::destroy(set& s, const std::string& name)
166 threads::arlock h(get_setting_lock());
167 auto state = group_internal_t::get_soft(&grp);
168 if(state)
169 state->settings.erase(name);
172 void group::xlistener::kill(set& s)
174 threads::arlock h(get_setting_lock());
175 auto state = group_internal_t::get_soft(&grp);
176 if(state)
177 state->sets_listened.erase(&s);
180 set::set()
184 set::~set()
186 auto state = set_internal_t::get_soft(this);
187 if(!state) return;
188 threads::arlock u(get_setting_lock());
189 //Call all DCBs on all factories.
190 for(auto i : state->supervars)
191 for(auto j : state->callbacks)
192 j->destroy(*this, i.first);
193 //Call all TCBs.
194 for(auto j : state->callbacks)
195 j->kill(*this);
196 //Notify all factories that base set died.
197 for(auto i : state->supervars)
198 i.second->set_died();
199 //We assume factories look after themselves, so we don't destroy those.
200 set_internal_t::clear(this);
203 void set::do_register(const std::string& name, superbase& info)
205 threads::arlock u(get_setting_lock());
206 auto& state = set_internal_t::get(this);
207 if(state.supervars.count(name)) {
208 std::cerr << "WARNING: Command collision for " << name << "!" << std::endl;
209 return;
211 state.supervars[name] = &info;
212 //Call all CCBs on this.
213 for(auto i : state.callbacks)
214 i->create(*this, name, info);
217 void set::do_unregister(const std::string& name, superbase& info)
219 threads::arlock u(get_setting_lock());
220 auto state = set_internal_t::get_soft(this);
221 if(!state) return;
222 if(!state->supervars.count(name) || state->supervars[name] != &info) return; //Not this.
223 state->supervars.erase(name);
224 //Call all DCBs on this.
225 for(auto i : state->callbacks)
226 i->destroy(*this, name);
229 void set::add_callback(set::listener& listener) throw(std::bad_alloc)
231 threads::arlock u(get_setting_lock());
232 auto& state = set_internal_t::get(this);
233 state.callbacks.insert(&listener);
234 //To avoid races, call CCBs on all factories for this.
235 for(auto j : state.supervars)
236 listener.create(*this, j.first, *j.second);
239 void set::drop_callback(set::listener& listener)
241 threads::arlock u(get_setting_lock());
242 auto state = set_internal_t::get_soft(this);
243 if(!state) return;
244 if(state->callbacks.count(&listener)) {
245 //To avoid races, call DCBs on all factories for this.
246 for(auto j : state->supervars)
247 listener.destroy(*this, j.first);
248 state->callbacks.erase(&listener);
252 base::base(group& _group, const std::string& _iname, const std::string& _hname, bool dynamic)
253 throw(std::bad_alloc)
254 : sgroup(&_group), iname(_iname), hname(_hname), is_dynamic(dynamic)
256 sgroup->do_register(iname, *this);
259 base::~base() throw()
261 threads::arlock u(get_setting_lock());
262 if(sgroup)
263 sgroup->do_unregister(iname, *this);
266 void base::group_died()
268 threads::arlock u(get_setting_lock());
269 sgroup = NULL;
270 if(is_dynamic) delete this;
273 void superbase::_superbase(set& _s, const std::string& _iname) throw(std::bad_alloc)
275 s = &_s;
276 iname = _iname;
277 s->do_register(iname, *this);
280 superbase::~superbase() throw()
282 threads::arlock u(get_setting_lock());
283 if(s)
284 s->do_unregister(iname, *this);
287 void superbase::set_died()
289 s = NULL;
292 cache::cache(group& _grp)
293 : grp(_grp)
297 std::map<std::string, std::string> cache::get_all()
299 std::map<std::string, std::string> x;
300 auto s = grp.get_settings_set();
301 for(auto i : s)
302 x[i] = grp[i].str();
303 for(auto i : badcache)
304 x[i.first] = i.second;
305 return x;
308 std::set<std::string> cache::get_keys()
310 return grp.get_settings_set();
313 void cache::set(const std::string& name, const std::string& value, bool allow_invalid)
314 throw(std::bad_alloc, std::runtime_error)
316 try {
317 grp[name].str(value);
318 threads::arlock u(get_setting_lock());
319 badcache.erase(name);
320 } catch(std::bad_alloc& e) {
321 throw;
322 } catch(std::exception& e) {
323 if(allow_invalid) {
324 threads::arlock u(get_setting_lock());
325 badcache[name] = value;
326 } else
327 throw;
331 std::string cache::get(const std::string& name) throw(std::bad_alloc, std::runtime_error)
333 return grp[name].str();
336 const description& cache::get_description(const std::string& name) throw(std::bad_alloc,
337 std::runtime_error)
339 return grp[name].get_description();
342 std::string cache::get_hname(const std::string& name) throw(std::bad_alloc, std::runtime_error)
344 return grp[name].get_hname();
347 const char* yes_no::enable = "yes";
348 const char* yes_no::disable = "no";