HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PDG_Service.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * COMMENTS:
7  */
8 
9 #ifndef __PDG_SERVICE_H__
10 #define __PDG_SERVICE_H__
11 
12 #include "PDG_API.h"
13 #include "PDG_Types.h"
14 
15 #include <PDGT/PDGT_ValueArgs.h>
16 
17 #include <UT/UT_Array.h>
18 #include <UT/UT_ConcurrentSet.h>
19 #include <UT/UT_Lock.h>
20 #include <UT/UT_StringArray.h>
21 #include <UT/UT_StringHolder.h>
22 #include <UT/UT_UniquePtr.h>
23 
24 class PDG_Scheduler;
25 class PY_PyObject;
26 class UT_Thread;
27 class UT_WorkBuffer;
28 
29 namespace PDGN
30 {
31  class PDGN_Message;
32  class PDGN_PollingClientNNG;
33 }
34 
35 /*
36  * Represents a registered service object, tracks the list of active clients
37  * for the service, and keeps track of various settings like the service
38  * pool size, MQ connection information, and service state.
39  */
41 {
42 public:
43  /// Service data format version
45 
46  /// Enumeration of return codes from acquireClient
48  {
49  /// A service client was successfully acquired
51 
52  /// A service client was successfully acquired and locked
54 
55  /// All service workers are already busy
57 
58  /// The service worker is aleady in use by a different task
59  /// with the same service lock id
61 
62  /// The service lock is invalid -- should never occur in practice
64  };
65 
66  /// Service client array entry
67  struct ClientInfo
68  {
69  /// Constructs a new client info instance
70  ClientInfo(
71  const PDG_Service* service,
72  const UT_StringHolder& server_address,
73  int server_port,
74  int client_num);
75 
76  /// Constructs an incomplete client instance -- this is used for
77  /// compatbility with older version of Houdini that did not
78  /// explicitly track client info
79  ClientInfo(const UT_StringHolder& client_name);
80 
81  /// Returns the command for the client as a string, but combining the
82  /// command arguments
83  UT_StringHolder commandString() const;
84 
85  /// Spawns the client as a child process, and returns true on
86  /// success
87  bool spawnProcess();
88 
89 
90  /// The array of command line args for the client
92 
93  /// The name of the service client
95 
96  /// The log path for client output
98 
99  /// The connection address
101 
102 
103  /// The connection port
105 
106  /// The process or job id for the client
108 
109  /// The total number of tasks the service has cooked
111 
112  /// The amount of memory in use by the process, as recorded by the
113  /// last task that cooked using the client
115 
116  /// The ID of the work item that has locked the service client, if
117  /// any
119 
120  /// Set to true once the client has connected
122 
123  /// Set to true if the client is active for a job
125  };
126 
127 public:
128 
129  /// Constructs a new service instance with a default pool size of 1, and
130  /// no connection information.
131  PDG_Service(
132  const PDG_BaseType* type,
133  const PDGT_ValueArgs& extra_args,
134  const UT_StringHolder& service_command,
135  bool internal_service);
136 
137  ~PDG_Service() override;
138 
139  /// Sets/gets the unique name for the service. The name of the service is
140  /// unique to the PDG_ServiceManager that created it.
142  { myName = name; }
143  const UT_StringHolder& name() const
144  { return myName; }
145 
146  /// Returns true if the service is internal to PDG. Internal services
147  /// cannot be directly managed by users.
148  bool isInternal() const
149  { return myIsInternal; }
150 
151  /// Sets/gets the persistent flag, which determines whether a service
152  /// should be persisted to the pdgservice JSON configuration file. A
153  /// service that isn't persistent will be lost when the Houdini session
154  /// is closed.
155  void setPersistent(bool persistent)
156  { myIsPersistent = persistent; }
157  bool isPersistent() const
158  { return myIsPersistent; }
159 
160  /// Returns true if the service has errors when starting
161  bool hasErrors() const
162  { return myHasErrors; }
163 
164  /// Sets/gets the autostart flag, which indicates that the service will
165  /// start automatically when work items need to use it.
166  void setAutoStart(bool auto_start)
167  { myIsAutoStart = auto_start; }
168  bool isAutoStart() const
169  { return myIsAutoStart; }
170 
171  /// Sets/gets an opaque Python data object that can be stored on the
172  /// service.
173  void setData(PY_PyObject* new_data);
175  { return myData; }
176 
177  /// Sets/gets the MQ url that the service should use to relay messages
178  /// between service clients and the PDG graph
179  void setMqUrl(const UT_StringHolder& url)
180  { myMqUrl = url; }
181  const UT_StringHolder& mqUrl() const
182  { return myMqUrl; }
183 
184  /// Sets/gets the log level for the MQ process that relays messages
185  /// between clients and the PDG graph
186  void setMqLogLevel(int log_level)
187  { myMqLogLevel = log_level; }
188  int mqLogLevel() const
189  { return myMqLogLevel; }
190 
191  /// Sets/gets the port used by the MQ relay.
192  void setMqPort(int mq_port)
193  { myMqPort = mq_port; }
194  int mqPort() const
195  { return myMqPort; }
196 
197  /// Sets/gets the directory that service clients should write their log
198  /// files to.
199  void setClientLogDir(const UT_StringHolder& log_dir)
200  { myClientLogDir = log_dir; }
202  { return myClientLogDir; }
203 
204  /// Sets/gets the directory that the MQ relay writes its logs to.
205  void setMqLogDir(const UT_StringHolder& log_dir)
206  { myMqLogDir = log_dir; }
207  const UT_StringHolder& mqLogDir() const
208  { return myMqLogDir; }
209 
210  /// Sets how the service itself should log output
212  { myClientLogType = log_type; }
214  { return myClientLogType; }
215 
216  /// Sets/gets the names for the environment variables to be set in
217  /// this job's environment. This only applies if the service does not
218  /// have a scheduler.
219  void setEnvVarNames(const UT_StringArray& env_var_names)
220  { myEnvVarNames = env_var_names; }
222  { return myEnvVarNames; }
223 
224  /// Sets/gets the values for the environment variables to be set in
225  /// this job's environment. This only applies if the service does not
226  /// have a scheduler.
227  void setEnvVarValues(const UT_StringArray& env_var_values)
228  { myEnvVarValues = env_var_values; }
230  { return myEnvVarValues; }
231 
232  /// For scheduler services, sets the name of the scheduler and graph
233  /// that created the service.
234  void setScheduler(
235  const UT_StringHolder& name,
236  const UT_StringHolder& context_name);
237 
238  /// Clears the scheduler information associated with a scheduler
239  /// service
240  void clearScheduler();
241 
242  /// Returns the name of the scheduler associated with the service, if
243  /// it's a scheduler service
245  { return mySchedulerName; }
246 
247  /// Returns the name of the graph that owns the scheduler that created
248  /// the service, if it's a scheduler service
250  { return mySchedulerContextName; }
251 
252  /// Sets/gets the service owner type -- either a session service or a
253  /// scheduler service
255  { myServiceOwner = owner; }
257  { return myServiceOwner; }
258 
259  /// Sets/gets the current state of the service
261  { myState = state; }
263  { return myState; }
264 
265  /// Sets/gets the command line string that should be used to start clients
266  /// running in the service pool
267  void setCommand(const UT_StringHolder& command)
268  { myCommand = command; }
269  const UT_StringHolder& command() const
270  { return myCommand; }
271 
272  /// Sets/gets the pool size for the service, which determines the number
273  /// of concurrent client processes that service executes
274  void setPoolSize(int pool_size)
275  { myPoolSize = pool_size; }
276  int poolSize() const
277  { return myPoolSize; }
278 
279  /// Sets/gets the connection port for the service itself
280  void setPort(int port)
281  { myPort = port; }
282  int port() const
283  { return myPort; }
284 
285  /// Sets the service connection timeout in milliseconds
286  void setConnectionTimeout(int ms)
287  { myConnectionTimeout = ms; }
288  int connectionTimeout() const
289  { return myConnectionTimeout; }
290 
291  /// Sets the memory limit and reset type for the service
293  PDG_ServiceResetType memory_reset_type)
294  {
295  myMemoryResetType = memory_reset_type;
296  }
298  exint memory_limit)
299  {
300  myMemoryLimit = memory_limit;
301  }
303  { return myMemoryResetType; }
305  { return myMemoryLimit; }
306 
307  /// Returns true if the service is start or is already running
308  bool isStartingOrRunning() const;
309 
310  /// Returns true if the service is stopping, or has already stopped
311  bool isStoppingOrStopped() const;
312 
313  /// Queries and returns the scheduler associated with the service, if the
314  /// service is a scheduler service
315  PDG_Scheduler* serviceScheduler(UT_WorkBuffer& errors) const;
316 
317 
318  /// Queries the list of all active service client names
319  UT_StringArray clientNames() const;
320 
321  /// Queries the name of a particular service client, using its client
322  /// number
323  UT_StringHolder clientName(int client_num) const;
324 
325  /// Returns the service client info for a particular client
326  ClientInfo* client(const UT_StringHolder& name);
327 
328  /// Adds a pending service client, which can be waiting on using
329  /// waitForClients
330  ClientInfo* addPendingClient(
331  const UT_StringHolder& server_address,
332  int server_port,
333  int client_num);
334 
335 
336  /// Stats a service pool. If background is true the service processes are
337  /// start asynchronously, and control immediately returns after calling
338  /// this function. It's up to the caller to ensure that the service has
339  /// started before using it. Returns either success, failure, or already-
340  /// running.
341  PDG_ServiceStartResult startService(
342  UT_WorkBuffer& errors,
343  bool background);
344 
345  /// Resets a specific service client, or all clients if empty string is
346  /// passed in as the client_name
347  bool resetClient(
348  UT_WorkBuffer& errors,
349  const UT_StringHolder& client_name =
351 
352  /// Restarts a service client. If client_name is empty string all
353  /// clients are restarted.
354  bool restartClient(
355  UT_WorkBuffer& errors,
356  const UT_StringHolder& client_name);
357 
358  /// Stops a service client and kills the process associated with it.
359  /// The client can be started again at a later point using restartClient.
360  /// If client_name is empty string all clients are stoppped.
361  bool stopClient(
362  UT_WorkBuffer& errors,
363  const UT_StringHolder& client_name);
364 
365  /// Stops the service if it's running, and returns a boolean to indiate
366  /// the status.
367  bool stopService(
368  UT_WorkBuffer& errors,
369  bool ignore_stopped);
370 
371  /// Cleans up the clients in the service. This method is public so that
372  /// schedulers can call it on services they manage.
373  bool cleanupService(
374  UT_WorkBuffer& errors,
375  bool stop_mq_server,
376  bool wait_for_clients);
377 
378  /// Submits a work item to a service client asynchronously, and returns
379  /// the pending message handle for the job.
380  PDGN::PDGN_Message* executeWorkItem(
381  UT_WorkBuffer& errors,
382  const UT_StringHolder& client_name,
383  const PDG_WorkItem* work_item);
384 
385  /// Submits a work item to a service client asychronously, and returns
386  /// the pending messae handle for the job. This variant of the method
387  /// allows the caller to supply a custom script that will run instead of
388  /// the standard job script or script data associated with the work item.
389  PDGN::PDGN_Message* executeWorkItem(
390  UT_WorkBuffer& errors,
391  const UT_StringHolder& client_name,
392  const PDG_WorkItem* work_item,
393  const UT_WorkBuffer& script_buffer);
394 
395  /// Starts a background thread that polls the ready listener until all
396  /// service clients have connected
397  void startPollingClient();
398 
399  /// This will force stop the ready listener. If you want to wait for it to
400  /// complete, you should call waitForClients() instead.
401  void stopPollingClient();
402 
403  /// Returns the name of the polling client, if one exists
404  const UT_StringHolder& pollingClientName() const;
405 
406  /// Returns true if the service has an active polling client
407  bool hasPollingClient() const
408  { return (myPollingClient != nullptr); }
409 
410  // Returns a pointer to the polling client, or nullptr if none exists.
411  PDGN::PDGN_PollingClientNNG*
413  { return myPollingClient.get(); }
414 
415  /// Waits for all pending service clients to connect, and then terminates
416  /// the ready listener
417  void waitForClients();
418 
419  /// Increments the number of actively starting client
420  void incrementStartingClientsCount();
421 
422  /// Returns the command for the service client with the specified client
423  /// number, as a string.
424  UT_StringHolder getCommand(
425  const char* server_address,
426  int server_port,
427  int client_num) const;
428 
429  /// Returns the command for the service client with the specified client
430  /// number, as an array of arguments
431  void getCommand(
432  UT_StringArray& command_tokens,
433  const char* server_address,
434  int server_port,
435  int client_num) const;
436 
437  /// Pings the specified client, and returns true if the client was
438  /// reachable
439  bool ping(int client_num, UT_WorkBuffer& errors) const;
440 
441  /// Acquires a client, which reserves it for the caller
442  ServiceAcquireResult acquireClient(
443  UT_StringHolder& client_name,
444  PDG_WorkItemID item_lock);
445 
446  /// Releases a client that was previously acquired, but does not unlock it
447  bool releaseClient(
448  UT_WorkBuffer& errors,
449  const UT_StringHolder& client_name,
450  PDG_ServiceResetWhen reset_when,
451  PDG_ServiceResetType reset_type,
452  int64 memory_usage);
453 
454  /// Unlocks a service client
455  bool unlockClient(
456  PDG_WorkItemID item_lock,
457  PDG_ServiceResetType reset_type,
458  UT_WorkBuffer& errors);
459 
460 private:
461  static void* runSpawnSessionService(void* param);
462  static void* runPollingThread(void* param);
463 
464  bool getClientNameFromMessage(
465  PDGN::PDGN_Message* msg,
466  UT_StringHolder& client_name);
467 
468  bool startSessionService(
469  UT_WorkBuffer& errors,
470  bool background);
471  bool spawnSessionServiceClients();
472  void pollClients();
473 
474  bool stopSessionService(UT_WorkBuffer& errors);
475  bool stopClient(
476  UT_WorkBuffer& errors,
477  int client_index,
478  bool restart);
479  bool resetClient(
480  UT_WorkBuffer& errors,
481  ClientInfo* client);
482  bool cleanupClients(
483  UT_WorkBuffer& errors,
484  bool stop_mq_server,
485  bool wait_for_clients);
486 
487 private:
488  static constexpr int theMaxConcurrentStartup = 24;
489  static constexpr int theSleepTime = 10;
490 
491  using PollingClient = UT_UniquePtr<PDGN::PDGN_PollingClientNNG>;
492  using ClientInfoArray = UT_Array<UT_UniquePtr<ClientInfo>>;
493 
494 private:
495  ClientInfoArray myPendingClients;
496  ClientInfoArray myActiveClients;
497  PDG_WorkItemIDSet myActiveLocks;
498 
499  UT_StringHolder myName;
500  UT_StringHolder myCommand;
501 
502  UT_StringHolder mySchedulerName;
503  UT_StringHolder mySchedulerContextName;
504 
505  UT_StringHolder myMqUrl;
506  UT_StringHolder myMqLogDir;
507 
508  UT_StringHolder myClientLogDir;
509 
510  UT_StringArray myEnvVarNames;
511  UT_StringArray myEnvVarValues;
512 
513  PollingClient myPollingClient;
514  UT_Thread* myPollingThread;
515  UT_Thread* myStartThread;
516 
517  PY_PyObject* myData;
518 
519  exint myMemoryLimit;
520 
521  pid_t myMqPid;
522 
523  SYS_AtomicInt<int> myPollingClientCount;
524 
525  int myConnectionTimeout;
526  int myMqLogLevel;
527  int myMqPort;
528  int myPoolSize;
529  int myPort;
530 
531  PDG_ServiceOwner myServiceOwner;
532  PDG_ServiceState myState;
533  PDG_ServiceLogType myClientLogType;
534 
535  PDG_ServiceResetType myMemoryResetType;
536 
537  bool myIsAutoStart;
538  bool myIsInternal;
539  bool myIsPersistent;
540  bool myHasErrors;
541 
542  UT_Lock myServiceLock;
543  mutable UT_Lock myClientLock;
544 };
545 
546 #endif
PDG_ServiceStartResult
Enumeration of possible result values from starting a service.
Definition: PDG_Types.h:236
void setState(PDG_ServiceState state)
Sets/gets the current state of the service.
Definition: PDG_Service.h:260
static const UT_StringHolder theDataVersion
Service data format version.
Definition: PDG_Service.h:44
A service client was successfully acquired.
Definition: PDG_Service.h:50
Service client array entry.
Definition: PDG_Service.h:67
PDG_WorkItemID myLock
Definition: PDG_Service.h:118
const UT_StringArray & envVarValues() const
Definition: PDG_Service.h:229
UT_StringHolder myName
The name of the service client.
Definition: PDG_Service.h:94
void setOwner(PDG_ServiceOwner owner)
Definition: PDG_Service.h:254
int mqPort() const
Definition: PDG_Service.h:194
void setName(const UT_StringHolder &name)
Definition: PDG_Service.h:141
#define PDG_API
Definition: PDG_API.h:23
exint myId
The process or job id for the client.
Definition: PDG_Service.h:107
int64 exint
Definition: SYS_Types.h:125
void setMqUrl(const UT_StringHolder &url)
Definition: PDG_Service.h:179
void setMemoryLimit(exint memory_limit)
Definition: PDG_Service.h:297
void setMqLogLevel(int log_level)
Definition: PDG_Service.h:186
The service lock is invalid – should never occur in practice.
Definition: PDG_Service.h:63
void setEnvVarNames(const UT_StringArray &env_var_names)
Definition: PDG_Service.h:219
bool hasErrors() const
Returns true if the service has errors when starting.
Definition: PDG_Service.h:161
bool isPersistent() const
Definition: PDG_Service.h:157
void setAutoStart(bool auto_start)
Definition: PDG_Service.h:166
const UT_StringHolder & schedulerName() const
Definition: PDG_Service.h:244
PDG_ServiceState
Enumeration of the different states that a service can be in.
Definition: PDG_Types.h:200
const UT_StringHolder & mqLogDir() const
Definition: PDG_Service.h:207
void setClientLogType(PDG_ServiceLogType log_type)
Sets how the service itself should log output.
Definition: PDG_Service.h:211
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
int poolSize() const
Definition: PDG_Service.h:276
void setConnectionTimeout(int ms)
Sets the service connection timeout in milliseconds.
Definition: PDG_Service.h:286
exint myTotalCooks
The total number of tasks the service has cooked.
Definition: PDG_Service.h:110
UT_StringHolder myAddress
The connection address.
Definition: PDG_Service.h:100
void setClientLogDir(const UT_StringHolder &log_dir)
Definition: PDG_Service.h:199
const UT_StringArray & envVarNames() const
Definition: PDG_Service.h:221
bool isAutoStart() const
Definition: PDG_Service.h:168
exint myPort
The connection port.
Definition: PDG_Service.h:104
void setEnvVarValues(const UT_StringArray &env_var_values)
Definition: PDG_Service.h:227
static const UT_StringHolder theEmptyString
A service client was successfully acquired and locked.
Definition: PDG_Service.h:53
PDG_ServiceOwner
Enumeration of the different types of service owner.
Definition: PDG_Types.h:188
PY_PyObject
int mqLogLevel() const
Definition: PDG_Service.h:188
UT_StringHolder myLogPath
The log path for client output.
Definition: PDG_Service.h:97
long long int64
Definition: SYS_Types.h:116
void setMqPort(int mq_port)
Sets/gets the port used by the MQ relay.
Definition: PDG_Service.h:192
ServiceAcquireResult
Enumeration of return codes from acquireClient.
Definition: PDG_Service.h:47
PDG_ServiceResetType memoryResetType() const
Definition: PDG_Service.h:302
GLuint const GLchar * name
Definition: glcorearb.h:786
bool isInternal() const
Definition: PDG_Service.h:148
PDG_ServiceResetType
Definition: PDG_Types.h:175
UT_StringArray myCommandArgs
The array of command line args for the client.
Definition: PDG_Service.h:91
void setMemoryResetType(PDG_ServiceResetType memory_reset_type)
Sets the memory limit and reset type for the service.
Definition: PDG_Service.h:292
const UT_StringHolder & command() const
Definition: PDG_Service.h:269
PDGN::PDGN_PollingClientNNG * getPollingClient() const
Definition: PDG_Service.h:412
All service workers are already busy.
Definition: PDG_Service.h:56
GLenum GLfloat param
Definition: glcorearb.h:104
void setPersistent(bool persistent)
Definition: PDG_Service.h:155
PDG_ServiceOwner owner() const
Definition: PDG_Service.h:256
void setCommand(const UT_StringHolder &command)
Definition: PDG_Service.h:267
const UT_StringHolder & mqUrl() const
Definition: PDG_Service.h:181
const UT_StringHolder & name() const
Definition: PDG_Service.h:143
const UT_StringHolder & schedulerContextName() const
Definition: PDG_Service.h:249
const UT_StringHolder & clientLogDir() const
Definition: PDG_Service.h:201
PDG_ServiceResetWhen
When service clients should be reset.
Definition: PDG_Types.h:158
exint memoryLimit() const
Definition: PDG_Service.h:304
bool myIsActive
Set to true if the client is active for a job.
Definition: PDG_Service.h:124
int connectionTimeout() const
Definition: PDG_Service.h:288
int port() const
Definition: PDG_Service.h:282
bool myIsConnected
Set to true once the client has connected.
Definition: PDG_Service.h:121
exint PDG_WorkItemID
Type defs for unique work item IDs.
Definition: PDG_Types.h:48
bool hasPollingClient() const
Returns true if the service has an active polling client.
Definition: PDG_Service.h:407
void setMqLogDir(const UT_StringHolder &log_dir)
Sets/gets the directory that the MQ relay writes its logs to.
Definition: PDG_Service.h:205
PDG_ServiceLogType
Enumeration of service log types.
Definition: PDG_Types.h:222
void setPoolSize(int pool_size)
Definition: PDG_Service.h:274
type
Definition: core.h:1059
PDG_ServiceState state() const
Definition: PDG_Service.h:262
PDG_ServiceLogType clientLogType() const
Definition: PDG_Service.h:213
PY_PyObject * data()
Definition: PDG_Service.h:174
void setPort(int port)
Sets/gets the connection port for the service itself.
Definition: PDG_Service.h:280