HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
FS_Background.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018
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  virtual ~BackgroundTimer() {}
155 
156  virtual const char *getClassName() const { return "BackgroundTimer"; }
157 
158  /// Poll time is in ms, so poll every 0.5 seconds
159  virtual int getPollTime() { return 500; }
160 
161  /// This callback is used to process events
162  virtual int processEvents()
163  {
164  pollTasks();
165  // When no more tasks, stop polling
166  if (!theTasks.entries())
167  stopPolling();
168  // Return true if we want to be called again
169  return theTasks.entries() > 0;
170  }
171 
172  /// Stop polling process
173  static void stopPolling()
174  {
175 #if defined(DEMO_DEBUG)
176  fprintf(stderr, "Stop polling\n");
177 #endif
178  if (theTimer)
179  {
180  // Stop events from being sent to a deleted
181  // object
182  theTimer->uninstallGenerator();
183  delete theTimer;
184  theTimer = NULL;
185  }
186  }
187  /// Start polling process
188  static void startPolling()
189  {
190  // Only create a timer if there isn't one already
191  if (!theTimer)
192  {
193 #if defined(DEMO_DEBUG)
194  fprintf(stderr, "Start polling\n");
195 #endif
196  theTimer = new BackgroundTimer();
197 
198  // Start the polling process
199  if (!theTimer->installGenerator())
200  {
201  delete theTimer;
202  theTimer = NULL;
203  }
204  }
205  }
206 private:
207  static BackgroundTimer *theTimer;
208 };
209 
210 BackgroundTimer *BackgroundTimer::theTimer = NULL;
211 
212 // Command manager callback to handle the @c demo_background command
213 static void
214 hdk_background(CMD_Args &args)
215 {
216  if (args.found('l'))
217  listTasks(args.out());
218  else if (args.argc() != 2)
219  {
220  args.err() << "Usage: " << args(0) << " [-l] 'command'\n";
221  args.err() << "Runs a command in the background\n";
222  args.err() << "The -l option lists all current commands\n";
223  }
224  else
225  {
226  BackgroundTask *task;
227  task = new BackgroundTask();
228  if (task->open(args(1)))
229  {
230  theTasks.append(task);
232  }
233  else
234  {
235  args.err() << "Error running: " << args(1) << "\n";
236  }
237  }
238 }
239 
240 } // end HDK Namespace
241 
242 /// CMDextendLibrary is the plug-in hook called to install extensions to the
243 /// CMD library.
244 void
246 {
247  // Install a new command
248  cman->installCommand("hdk_background", "l", HDK_Sample::hdk_background);
249 }
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:42
int found(int opt) const
Definition: UT_Args.h:56
void uninstallGenerator()
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.
png_uint_32 i
Definition: png.h:2877
void close()
Close the pipe, blocking until the pipe is complete.
Definition: FS_Background.C:80
std::ostream & out()
Definition: CMD_Args.h:32
png_FILE_p fp
Definition: png.h:2028
virtual int getPollTime()
Poll time is in ms, so poll every 0.5 seconds.
void harden()
Take shallow copy and make it deep.
Definition: UT_String.h:213
int argc() const
Definition: UT_Args.h:43
static void startPolling()
Start polling process.
virtual int processEvents()
This callback is used to process events.
int isComplete(int block=0)
BackgroundTask is the object which keeps track of the background process.
Definition: FS_Background.C:44
void CMDextendLibrary(CMD_Manager *cman)
virtual const char * getClassName() const
static void stopPolling()
Stop polling process.
int close(int wait_for_child_to_terminate=0)
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:794