Skip to content

Commit

Permalink
Merge pull request #737 from zacikpa/functions-fix
Browse files Browse the repository at this point in the history
Fix races occuring when two threads expand variables at the same time
  • Loading branch information
yarda authored Feb 2, 2025
2 parents c624ba6 + 64166c5 commit 374697b
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 47 deletions.
8 changes: 4 additions & 4 deletions compile_plugin_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import argparse
import os
import inspect
from tuned.utils.plugin_loader import PluginLoader
from tuned.utils.class_loader import ClassLoader
from tuned.plugins.base import Plugin


class DocLoader(PluginLoader):
class PluginDocLoader(ClassLoader):
def __init__(self):
super(DocLoader, self).__init__()
super(PluginDocLoader, self).__init__()

def _set_loader_parameters(self):
self._namespace = "tuned.plugins"
Expand All @@ -23,7 +23,7 @@ def _set_loader_parameters(self):
with open(args.intro, "r") as intro_file:
intro = intro_file.read()

all_plugins = sorted(DocLoader().load_all_plugins(), key=lambda x: x.__module__)
all_plugins = sorted(PluginDocLoader().load_all_classes(), key=lambda x: x.__module__)

with open(args.out, "w") as out_file:
out_file.write(intro)
Expand Down
6 changes: 3 additions & 3 deletions tuned/daemon/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,12 @@ def _get_startup_profile(self):

def get_all_plugins(self):
"""Return all accessible plugin classes"""
return self._unit_manager.plugins_repository.load_all_plugins()
return self._unit_manager.plugins_repository.load_all_classes()

def get_plugin_documentation(self, plugin_name):
"""Return plugin class docstring"""
try:
plugin_class = self._unit_manager.plugins_repository.load_plugin(
plugin_class = self._unit_manager.plugins_repository.load_class(
plugin_name
)
except ImportError:
Expand All @@ -313,7 +313,7 @@ def get_plugin_hints(self, plugin_name):
dictionary -- {parameter_name: hint}
"""
try:
plugin_class = self._unit_manager.plugins_repository.load_plugin(
plugin_class = self._unit_manager.plugins_repository.load_class(
plugin_name
)
except ImportError:
Expand Down
6 changes: 3 additions & 3 deletions tuned/monitors/repository.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import tuned.logs
import tuned.monitors
from tuned.utils.plugin_loader import PluginLoader
from tuned.utils.class_loader import ClassLoader

log = tuned.logs.get()

__all__ = ["Repository"]

class Repository(PluginLoader):
class Repository(ClassLoader):

def __init__(self):
super(Repository, self).__init__()
Expand All @@ -23,7 +23,7 @@ def _set_loader_parameters(self):

def create(self, plugin_name, devices):
log.debug("creating monitor %s" % plugin_name)
monitor_cls = self.load_plugin(plugin_name)
monitor_cls = self.load_class(plugin_name)
monitor_instance = monitor_cls(devices)
self._monitors.add(monitor_instance)
return monitor_instance
Expand Down
6 changes: 3 additions & 3 deletions tuned/plugins/repository.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from tuned.utils.plugin_loader import PluginLoader
from tuned.utils.class_loader import ClassLoader
import tuned.plugins.base
import tuned.logs

log = tuned.logs.get()

__all__ = ["Repository"]

class Repository(PluginLoader):
class Repository(ClassLoader):

def __init__(self, monitor_repository, storage_factory, hardware_inventory, device_matcher, device_matcher_udev, plugin_instance_factory, global_cfg, variables):
super(Repository, self).__init__()
Expand All @@ -31,7 +31,7 @@ def _set_loader_parameters(self):

def create(self, plugin_name):
log.debug("creating plugin %s" % plugin_name)
plugin_cls = self.load_plugin(plugin_name)
plugin_cls = self.load_class(plugin_name)
plugin_instance = plugin_cls(self._monitor_repository, self._storage_factory, self._hardware_inventory, self._device_matcher,\
self._device_matcher_udev, self._plugin_instance_factory, self._global_cfg, self._variables)
self._plugins.add(plugin_instance)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
import os
import re
import glob
from . import repository
import tuned.logs
import tuned.consts as consts
from tuned.utils.commands import commands

log = tuned.logs.get()

cmd = commands()

class Functions():
class Parser():
"""
Built-in functions
Parser used for expanding strings containing functions.
"""

def __init__(self):
self._repository = repository.Repository()
self._parse_init()
def __init__(self, repository):
self._repository = repository

def _parse_init(self, s = ""):
self._cnt = 0
Expand Down
21 changes: 14 additions & 7 deletions tuned/profiles/functions/repository.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from tuned.utils.plugin_loader import PluginLoader
from . import base
from tuned.utils.class_loader import ClassLoader
from tuned.profiles.functions.parser import Parser
from tuned.profiles.functions.base import Function
import tuned.logs
import tuned.consts as consts
from tuned.utils.commands import commands

log = tuned.logs.get()

class Repository(PluginLoader):
class Repository(ClassLoader):
"""
Repository of functions used within TuneD profiles.
The functions are loaded lazily (when first used).
"""

def __init__(self):
super(Repository, self).__init__()
Expand All @@ -19,16 +23,16 @@ def functions(self):
def _set_loader_parameters(self):
self._namespace = "tuned.profiles.functions"
self._prefix = consts.FUNCTION_PREFIX
self._interface = tuned.profiles.functions.base.Function
self._interface = Function

def create(self, function_name):
log.debug("creating function %s" % function_name)
function_cls = self.load_plugin(function_name)
function_cls = self.load_class(function_name)
function_instance = function_cls()
self._functions[function_name] = function_instance
return function_instance

# loads function from plugin file and return it
# load a function from its file and return it
# if it is already loaded, just return it, it is not loaded again
def load_func(self, function_name):
if not function_name in self._functions:
Expand All @@ -41,3 +45,6 @@ def delete(self, function):
for k, v in list(self._functions.items()):
if v == function:
del self._functions[k]

def expand(self, s):
return Parser(self).expand(s)
4 changes: 2 additions & 2 deletions tuned/profiles/variables.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os
import re
import tuned.logs
from .functions import functions as functions
import tuned.consts as consts
from tuned.profiles import functions
from tuned.utils.commands import commands
from tuned.utils.config_parser import ConfigParser, Error

Expand All @@ -17,7 +17,7 @@ def __init__(self):
self._cmd = commands()
self._lookup_re = {}
self._lookup_env = {}
self._functions = functions.Functions()
self._functions = functions.Repository()

def _add_env_prefix(self, s, prefix):
if s.find(prefix) == 0:
Expand Down
33 changes: 17 additions & 16 deletions tuned/utils/plugin_loader.py → tuned/utils/class_loader.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import tuned.logs
import os

__all__ = ["PluginLoader"]
__all__ = ["ClassLoader"]

log = tuned.logs.get()

class PluginLoader(object):
class ClassLoader(object):
__slots__ = ["_namespace", "_prefix", "_interface"]

def _set_loader_parameters(self):
Expand All @@ -16,7 +16,7 @@ def _set_loader_parameters(self):
raise NotImplementedError()

def __init__(self):
super(PluginLoader, self).__init__()
super(ClassLoader, self).__init__()

self._namespace = None
self._prefix = None
Expand All @@ -26,9 +26,9 @@ def __init__(self):
assert type(self._prefix) is str
assert type(self._interface) is type and issubclass(self._interface, object)

def load_plugin(self, plugin_name):
assert type(plugin_name) is str
module_name = "%s.%s%s" % (self._namespace, self._prefix, plugin_name)
def load_class(self, class_name):
assert type(class_name) is str
module_name = "%s.%s%s" % (self._namespace, self._prefix, class_name)
return self._get_class(module_name)

def _get_class(self, module_name):
Expand All @@ -45,22 +45,23 @@ def _get_class(self, module_name):
if type(cls) is type and issubclass(cls, self._interface):
return cls

raise ImportError("Cannot find the plugin class.")
raise ImportError("Cannot find the class %s." % module_name)

def load_all_plugins(self):
plugins_package = __import__(self._namespace)
plugin_clss = []
for module_name in os.listdir(plugins_package.plugins.__path__[0]):
def load_all_classes(self):
package = __import__(self._namespace)
basename = self._namespace.split(".")[-1]
classes = []
for module_name in os.listdir(getattr(package, basename).__path__[0]):
try:
module_name = os.path.splitext(module_name)[0]
if not module_name.startswith("plugin_"):
if not module_name.startswith(self._prefix):
continue
plugin_class = self._get_class(
next_class = self._get_class(
"%s.%s" % (self._namespace, module_name)
)
if plugin_class not in plugin_clss:
plugin_clss.append(plugin_class)
if next_class not in classes:
classes.append(next_class)
except ImportError:
pass
return plugin_clss
return classes

0 comments on commit 374697b

Please sign in to comment.