1 /*
2  *  Copyright (C) 2013-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #pragma once
10 
11 #include "addons/IAddon.h"
12 #include "interfaces/generic/ILanguageInvoker.h"
13 #include "threads/CriticalSection.h"
14 
15 #include <map>
16 #include <memory>
17 #include <set>
18 #include <vector>
19 
20 class CLanguageInvokerThread;
21 typedef std::shared_ptr<CLanguageInvokerThread> CLanguageInvokerThreadPtr;
22 
23 class CScriptInvocationManager
24 {
25 public:
26   static CScriptInvocationManager& GetInstance();
27 
28   void Process();
29   void Uninitialize();
30 
31   void RegisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler, const std::string &extension);
32   void RegisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler, const std::set<std::string> &extensions);
33   void UnregisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler);
34   bool HasLanguageInvoker(const std::string &script) const;
35   LanguageInvokerPtr GetLanguageInvoker(const std::string& script);
36 
37   /*!
38   * \brief Returns addon_handle if last reusable invoker is ready to use.
39   */
40   int GetReusablePluginHandle(const std::string& script);
41 
42   /*!
43    * \brief Executes the given script asynchronously in a separate thread.
44    *
45    * \param script Path to the script to be executed
46    * \param addon (Optional) Addon to which the script belongs
47    * \param arguments (Optional) List of arguments passed to the script
48    * \return -1 if an error occurred, otherwise the ID of the script
49    */
50   int ExecuteAsync(const std::string& script,
51                    const ADDON::AddonPtr& addon = ADDON::AddonPtr(),
52                    const std::vector<std::string>& arguments = std::vector<std::string>(),
53                    bool reuseable = false,
54                    int pluginHandle = -1);
55   /*!
56   * \brief Executes the given script asynchronously in a separate thread.
57   *
58   * \param script Path to the script to be executed
59   * \param languageInvoker Language invoker to be used to execute the script
60   * \param addon (Optional) Addon to which the script belongs
61   * \param arguments (Optional) List of arguments passed to the script
62   * \return -1 if an error occurred, otherwise the ID of the script
63   */
64   int ExecuteAsync(const std::string& script,
65                    const LanguageInvokerPtr& languageInvoker,
66                    const ADDON::AddonPtr& addon = ADDON::AddonPtr(),
67                    const std::vector<std::string>& arguments = std::vector<std::string>(),
68                    bool reuseable = false,
69                    int pluginHandle = -1);
70 
71   /*!
72   * \brief Executes the given script synchronously.
73   *
74   * \details The script is actually executed asynchronously but the calling
75   * thread is blocked until either the script has finished or the given timeout
76   * has expired. If the given timeout has expired the script's execution is
77   * stopped and depending on the specified wait behaviour we wait for the
78   * script's execution to finish or not.
79   *
80   * \param script Path to the script to be executed
81   * \param addon (Optional) Addon to which the script belongs
82   * \param arguments (Optional) List of arguments passed to the script
83   * \param timeout (Optional) Timeout (in milliseconds) for the script's execution
84   * \param waitShutdown (Optional) Whether to wait when having to forcefully stop the script's execution or not.
85   * \return -1 if an error occurred, 0 if the script terminated or ETIMEDOUT if the given timeout expired
86   */
87   int ExecuteSync(const std::string& script,
88                   const ADDON::AddonPtr& addon = ADDON::AddonPtr(),
89                   const std::vector<std::string>& arguments = std::vector<std::string>(),
90                   uint32_t timeoutMs = 0,
91                   bool waitShutdown = false);
92   /*!
93   * \brief Executes the given script synchronously.
94   *
95   * \details The script is actually executed asynchronously but the calling
96   * thread is blocked until either the script has finished or the given timeout
97   * has expired. If the given timeout has expired the script's execution is
98   * stopped and depending on the specified wait behaviour we wait for the
99   * script's execution to finish or not.
100   *
101   * \param script Path to the script to be executed
102   * \param languageInvoker Language invoker to be used to execute the script
103   * \param addon (Optional) Addon to which the script belongs
104   * \param arguments (Optional) List of arguments passed to the script
105   * \param timeout (Optional) Timeout (in milliseconds) for the script's execution
106   * \param waitShutdown (Optional) Whether to wait when having to forcefully stop the script's execution or not.
107   * \return -1 if an error occurred, 0 if the script terminated or ETIMEDOUT if the given timeout expired
108   */
109   int ExecuteSync(const std::string& script,
110                   const LanguageInvokerPtr& languageInvoker,
111                   const ADDON::AddonPtr& addon = ADDON::AddonPtr(),
112                   const std::vector<std::string>& arguments = std::vector<std::string>(),
113                   uint32_t timeoutMs = 0,
114                   bool waitShutdown = false);
115   bool Stop(int scriptId, bool wait = false);
116   bool Stop(const std::string &scriptPath, bool wait = false);
117 
118   /*!
119    *\brief Stop all running scripts
120    *\param wait if kodi should wait for each script to finish (default false)
121   */
122   void StopRunningScripts(bool wait = false);
123 
124   bool IsRunning(int scriptId) const;
125   bool IsRunning(const std::string& scriptPath) const;
126 
127 protected:
128   friend class CLanguageInvokerThread;
129 
130   void OnExecutionDone(int scriptId);
131 
132 private:
133   CScriptInvocationManager() = default;
134   CScriptInvocationManager(const CScriptInvocationManager&) = delete;
135   CScriptInvocationManager const& operator=(CScriptInvocationManager const&) = delete;
136   virtual ~CScriptInvocationManager();
137 
138   typedef struct {
139     CLanguageInvokerThreadPtr thread;
140     std::string script;
141     bool done;
142   } LanguageInvokerThread;
143   typedef std::map<int, LanguageInvokerThread> LanguageInvokerThreadMap;
144   typedef std::map<std::string, ILanguageInvocationHandler*> LanguageInvocationHandlerMap;
145 
146   LanguageInvokerThread getInvokerThread(int scriptId) const;
147 
148   LanguageInvocationHandlerMap m_invocationHandlers;
149   LanguageInvokerThreadMap m_scripts;
150   CLanguageInvokerThreadPtr m_lastInvokerThread;
151   int m_lastPluginHandle = -1;
152 
153   std::map<std::string, int> m_scriptPaths;
154   int m_nextId = 0;
155   mutable CCriticalSection m_critSection;
156 };
157