HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FS_Background.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *----------------------------------------------------------------------------
26  */
27 
28 #include <UT/UT_DSOVersion.h>
29 #include <UT/UT_WritePipe.h>
30 #include <FS/FS_EventGenerator.h>
31 #include <CMD/CMD_Args.h>
32 #include <CMD/CMD_Manager.h>
33 
34 //#define DEMO_DEBUG
35 
36 namespace HDK_Sample {
37 
38 /// @file
39 /// This example uses UT_WritePipe to open a pipe to an external process, then
40 /// use FS_EventGenerator to poll the list of running background tasks.
41 /// @see UT_WritePipe, FS_EventGenerator, CMD_Manager, CMD_Args
42 
43 /// BackgroundTask is the object which keeps track of the background process
45 public:
46  BackgroundTask() : myPipe(NULL) {}
48 
49  /// Open a pipe to an external application
50  /// @param cmd The command to open
51  /// @param text The text to write to the pipe
52  /// @param length The length of the @c text
53  FILE *open(const char *cmd, char *text=0, int length=0)
54  {
55  FILE *fp;
56 
57  myCommand.harden(cmd);
58  if (myPipe)
59  close(); // Close existing pipe
60  myPipe = new UT_WritePipe();
61  fp = myPipe->open(cmd);
62  if (fp && text && length > 0)
63  {
64  // Due to the way operating systems work, the
65  // pipe may block at this point -- until the
66  // external application reads the data on the
67  // pipe that is.
68  if (fwrite(text, 1, length, fp) != length)
69  {
70  close();
71  fp = NULL;
72  }
73  else
74  fflush(fp);
75  }
76  return fp;
77  }
78 
79  /// Close the pipe, blocking until the pipe is complete
80  void close()
81  {
82  if (myPipe)
83  {
84  myPipe->close(true);
85  delete myPipe;
86  myPipe = NULL;
87  }
88  }
89 
90  /// Test if the background process is complete
91  bool isComplete()
92  {
93  return !myPipe || myPipe->isComplete(false);
94  }
95 
98 };
99 
100 static UT_ValArray<BackgroundTask *> theTasks;
101 
102 static void
103 pollTasks()
104 {
105  int i;
106 #if defined(DEMO_DEBUG)
107  fprintf(stderr, "Poll: %d\n", theTasks.entries());
108 #endif
109  for (i = 0; i < theTasks.entries(); ++i)
110  {
111  if (theTasks(i)->isComplete())
112  {
113 #if defined(DEMO_DEBUG)
114  if (theTasks(i)->myPipe)
115  fprintf(stderr, "Delete [%d] %s\n",
116  theTasks(i)->myPipe->getPid(),
117  (const char *)theTasks(i)->myCommand);
118 #endif
119  delete theTasks(i);
120  theTasks(i) = 0;
121  }
122  }
123  theTasks.collapse();
124 }
125 
126 /// Function to list background tasks to a stream
127 static void
128 listTasks(std::ostream &os)
129 {
130  int i;
131  os << theTasks.entries() << " background tasks\n";
132  for (i = 0; i < theTasks.entries(); i++)
133  {
134  if (theTasks(i)->myPipe)
135  {
136  os << i
137  << " pid[" << theTasks(i)->myPipe->getPid()
138  << "] " << theTasks(i)->myCommand
139  << "\n";
140  }
141  }
142 }
143 
144 /// @brief Event generator to poll for completed tasks
145 ///
146 /// When Houdini has an opportunity, it will call this class to process any
147 /// events. Ideally, we would use the FS_EventGenerator::getFileDescriptor()
148 /// method to return a file descriptor which allows a select() call to be used
149 /// rather than a polling process. However, in this case, we need to poll the
150 /// background processes.
152 public:
154  ~BackgroundTimer() override {}
155 
156  const char *getClassName() const override
157  { return "BackgroundTimer"; }
158 
159  /// Poll time is in ms, so poll every 0.5 seconds
160  int getPollTime() override { return 500; }
161 
162  /// This callback is used to process events
163  int processEvents() override
164  {
165  pollTasks();
166  // When no more tasks, stop polling
167  if (!theTasks.entries())
168  stopPolling();
169  // Return true if we want to be called again
170  return theTasks.entries() > 0;
171  }
172 
173  /// Stop polling process
174  static void stopPolling()
175  {
176 #if defined(DEMO_DEBUG)
177  fprintf(stderr, "Stop polling\n");
178 #endif
179  if (theTimer)
180  {
181  // Stop events from being sent to a deleted
182  // object
183  theTimer->uninstallGenerator();
184  delete theTimer;
185  theTimer = NULL;
186  }
187  }
188  /// Start polling process
189  static void startPolling()
190  {
191  // Only create a timer if there isn't one already
192  if (!theTimer)
193  {
194 #if defined(DEMO_DEBUG)
195  fprintf(stderr, "Start polling\n");
196 #endif
197  theTimer = new BackgroundTimer();
198 
199  // Start the polling process
200  if (!theTimer->installGenerator())
201  {
202  delete theTimer;
203  theTimer = NULL;
204  }
205  }
206  }
207 private:
208  static BackgroundTimer *theTimer;
209 };
210 
211 BackgroundTimer *BackgroundTimer::theTimer = NULL;
212 
213 // Command manager callback to handle the @c demo_background command
214 static void
215 hdk_background(CMD_Args &args)
216 {
217  if (args.found('l'))
218  listTasks(args.out());
219  else if (args.argc() != 2)
220  {
221  args.err() << "Usage: " << args(0) << " [-l] 'command'\n";
222  args.err() << "Runs a command in the background\n";
223  args.err() << "The -l option lists all current commands\n";
224  }
225  else
226  {
227  BackgroundTask *task;
228  task = new BackgroundTask();
229  if (task->open(args(1)))
230  {
231  theTasks.append(task);
233  }
234  else
235  {
236  args.err() << "Error running: " << args(1) << "\n";
237  }
238  }
239 }
240 
241 } // end HDK Namespace
242 
243 /// CMDextendLibrary is the plug-in hook called to install extensions to the
244 /// CMD library.
245 void
247 {
248  // Install a new command
249  cman->installCommand("hdk_background", "l", HDK_Sample::hdk_background);
250 }
Event generator to poll for completed tasks.
FILE * open(const char *cmd, char *text=0, int length=0)
Definition: FS_Background.C:53
std::ostream & err(int show_line_number=1)
FILE * open(const char *cmd)
Definition: UT_WritePipe.h:52
int found(int opt) const
Definition: UT_Args.h:62
void uninstallGenerator()
bool isComplete(bool block=false)
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
bool isComplete()
Test if the background process is complete.
Definition: FS_Background.C:91
void installCommand(const char *name, const char *options, CMD_Callback cb, bool is_safe=true)
Install a command into the command list.
int getPollTime() override
Poll time is in ms, so poll every 0.5 seconds.
void close()
Close the pipe, blocking until the pipe is complete.
Definition: FS_Background.C:80
std::ostream & out()
Definition: CMD_Args.h:30
void harden()
Take shallow copy and make it deep.
Definition: UT_String.h:215
int argc() const
Definition: UT_Args.h:49
static void startPolling()
Start polling process.
int close(bool wait_for_child_to_terminate=false)
auto fprintf(std::FILE *f, const S &fmt, const T &...args) -> int
Definition: printf.h:602
const char * getClassName() const override
BackgroundTask is the object which keeps track of the background process.
Definition: FS_Background.C:44
void CMDextendLibrary(CMD_Manager *cman)
**If you just want to fire and args
Definition: thread.h:609
static void stopPolling()
Stop polling process.
int processEvents() override
This callback is used to process events.