diff --git a/common.py b/common.py index dbe5414..51e40a1 100644 --- a/common.py +++ b/common.py @@ -7,6 +7,7 @@ import time import requests from collections import namedtuple from urllib.error import URLError +import threading RATE_NO_LIMIT = 0x00 RATE_GLOBAL = 0x01 @@ -43,6 +44,8 @@ buckets = { rate_limit_classes = buckets.keys() +plugin_lock = threading.Lock() + def rate_limit(rate_class=RATE_GLOBAL): """ @@ -182,6 +185,23 @@ def giphy(subject, api_key): return giphy_url +def config_locked(f): + """A decorator that makes access to the config thread-safe""" + + def decorate(*args, **kwargs): + + plugin_lock.acquire() + + try: + return f(*args, **kwargs) + except: + raise + finally: + plugin_lock.release() + + return decorate + + def pluginfunction(name, desc, plugin_type, ratelimit_class=RATE_GLOBAL, enabled=True): """A decorator to make a plugin out of a function :param enabled: diff --git a/local_config.ini.spec b/local_config.ini.spec index 8eef51d..fcb5898 100644 --- a/local_config.ini.spec +++ b/local_config.ini.spec @@ -14,7 +14,6 @@ hist_max_count = integer(default=5) hist_max_time = integer(default=10*60) persistent_storage = string(default='urlbot.persistent') -persistent_locked = boolean(default=false) # the "dice" feature will use more efficient random data (0) for given users enhanced-random-user = string_list(default=list()) diff --git a/plugins/__init__.py b/plugins/__init__.py index a654b84..ae35b34 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -5,7 +5,7 @@ import traceback import types import config -from common import RATE_NO_LIMIT, pluginfunction, ptypes_PARSE, ptypes_COMMAND, ptypes_MUC_ONLINE, ptypes +from common import RATE_NO_LIMIT, pluginfunction, config_locked, ptypes_PARSE, ptypes_COMMAND, ptypes_MUC_ONLINE, ptypes from plugins import commands, parsers, muc_online joblist = [] @@ -21,18 +21,14 @@ def plugin_enabled_get(urlbot_plugin): return urlbot_plugin.is_enabled +@config_locked def plugin_enabled_set(plugin, enabled): - if config.conf_get('persistent_locked'): - log.warn("couldn't get exclusive lock") - - config.conf_set('persistent_locked', True) if plugin.plugin_name not in config.runtime_config_store['plugins']: config.runtime_config_store['plugins'][plugin.plugin_name] = {} config.runtime_config_store['plugins'][plugin.plugin_name]['enabled'] = enabled config.runtimeconf_persist() - config.conf_set('persistent_locked', False) def register_active_event(t, callback, args, action_runner, plugin, msg_obj): diff --git a/plugins/commands.py b/plugins/commands.py index 4417130..93b3135 100644 --- a/plugins/commands.py +++ b/plugins/commands.py @@ -13,7 +13,7 @@ from lxml import etree import config from common import ( VERSION, RATE_FUN, RATE_GLOBAL, RATE_INTERACTIVE, RATE_NO_LIMIT, - giphy, pluginfunction, + giphy, pluginfunction, config_locked, ptypes_COMMAND, RATE_NO_SILENCE, get_nick_from_object @@ -340,6 +340,7 @@ def usersetting_get(argv, args): @pluginfunction('set', 'modify a user setting', ptypes_COMMAND, ratelimit_class=RATE_NO_LIMIT) +@config_locked def command_usersetting(argv, **args): settings = ['spoiler'] arg_user = args['reply_user'] @@ -360,20 +361,12 @@ def command_usersetting(argv, **args): # display current value return usersetting_get(argv, args) - if config.conf_get('persistent_locked'): - return { - 'msg': args['reply_user'] + ''': couldn't get exclusive lock''' - } - - config.conf_set('persistent_locked', True) - if arg_user not in config.runtime_config_store['user_pref']: config.runtime_config_store['user_pref'][arg_user] = {} config.runtime_config_store['user_pref'][arg_user][arg_key] = 'on' == arg_val config.runtimeconf_persist() - config.conf_set('persistent_locked', False) # display value written to db return usersetting_get(argv, args) @@ -482,6 +475,7 @@ def command_show_moinlist(argv, **args): @pluginfunction( 'record', 'record a message for a now offline user (usage: record {user} {some message};' ' {some message} == "previous" to use the last channel message)', ptypes_COMMAND) +@config_locked def command_record(argv, **args): if len(argv) < 2: return { @@ -497,20 +491,12 @@ def command_record(argv, **args): else: message += ' '.join(argv[1:]) - if config.conf_get('persistent_locked'): - return { - 'msg': "%s: couldn't get exclusive lock" % args['reply_user'] - } - - config.conf_set('persistent_locked', True) - if target_user not in config.runtime_config_store['user_records']: config.runtime_config_store['user_records'][target_user] = [] config.runtime_config_store['user_records'][target_user].append(message) config.runtimeconf_persist() - config.conf_set('persistent_locked', False) return { 'msg': '%s: message saved for %s' % (args['reply_user'], target_user) diff --git a/plugins/muc_online.py b/plugins/muc_online.py index d9f14fc..9b97aa1 100644 --- a/plugins/muc_online.py +++ b/plugins/muc_online.py @@ -2,7 +2,7 @@ import logging import config from common import ( - pluginfunction, + pluginfunction, config_locked, ptypes_MUC_ONLINE ) @@ -10,6 +10,7 @@ log = logging.getLogger(__name__) @pluginfunction('send_record', 'delivers previously saved message to user', ptypes_MUC_ONLINE) +@config_locked def send_record(**args): arg_user = args['reply_user'] arg_user_key = arg_user.lower() @@ -31,15 +32,7 @@ def send_record(**args): ) } - if config.conf_get('persistent_locked'): - log.warning("couldn't get exclusive lock") - return None - - config.conf_set('persistent_locked', True) - user_records.pop(arg_user_key) config.runtimeconf_persist() - config.conf_set('persistent_locked', False) - return response