fe76e22009-07-13Henrik Grubbström (Grubba) // // Basic filesystem monitor. //
b0a5fc2009-10-19Henrik Grubbström (Grubba) // $Id: basic.pike,v 1.28 2009/10/19 14:23:45 grubba Exp $
fe76e22009-07-13Henrik Grubbström (Grubba) // // 2009-07-09 Henrik Grubbström //
90569b2009-07-13Henrik Grubbström (Grubba) //! Basic filesystem monitor. //! //! This module is intended to be used for incremental scanning of //! a filesystem.
fe76e22009-07-13Henrik Grubbström (Grubba) //! The default maximum number of seconds between checks of directories //! in seconds. //! //! This value is multiplied with @[default_file_interval_factor] to //! get the corresponding default maximum number of seconds for files. //! //! The value can be changed by calling @[create()]. //! //! The value can be overridden for individual files or directories //! by calling @[monitor()]. //! //! Overload this constant to change the default. protected constant default_max_dir_check_interval = 60; //! The default factor to multiply @[default_max_dir_check_interval] //! with to get the maximum number of seconds between checks of files. //! //! The value can be changed by calling @[create()]. //! //! The value can be overridden for individual files or directories //! by calling @[monitor()]. //! //! Overload this constant to change the default. protected constant default_file_interval_factor = 5;
075cf22009-07-16Henrik Grubbström (Grubba) //! The default minimum number of seconds without changes for a change //! to be regarded as stable (see @[stable_data_change()].
f3483e2009-07-16Henrik Grubbström (Grubba) protected constant default_stable_time = 5;
fe76e22009-07-13Henrik Grubbström (Grubba)  protected int max_dir_check_interval = default_max_dir_check_interval; protected int file_interval_factor = default_file_interval_factor;
f3483e2009-07-16Henrik Grubbström (Grubba) protected int stable_time = default_stable_time;
fe76e22009-07-13Henrik Grubbström (Grubba)  // Callbacks //! File content changed callback. //! //! @param path //! Path of the file which has had content changed. //! //! This function is called when a change has been detected for a //! monitored file. //! //! Called by @[check()] and @[check_monitor()]. //! //! Overload this to do something useful. void data_changed(string path); //! File attribute changed callback. //! //! @param path //! Path of the file or directory which has changed attributes. //! //! @param st //! Status information for @[path] as obtained by @expr{file_stat(path, 1)@}. //! //! This function is called when a change has been detected for an //! attribute for a monitored file or directory. //! //! Called by @[check()] and @[check_monitor()]. //! //! @note //! If there is a @[data_changed()] callback, it may supersede this //! callback if the file content also has changed. //! //! Overload this to do something useful. void attr_changed(string path, Stdio.Stat st);
249a3f2009-07-13Henrik Grubbström (Grubba) //! File existance callback. //! //! @param path //! Path of the file or directory. //! //! @param st //! Status information for @[path] as obtained by @expr{file_stat(path, 1)@}. //! //! This function is called during initialization for all monitored paths, //! and subpaths for monitored directories. It represents the initial state //! for the monitor. //! //! @note //! For directories, @[file_created()] will be called for the subpaths //! before the call for the directory itself. This can be used to detect //! when the initialization for a directory is finished. //! //! Called by @[check()] and @[check_monitor()] the first time a monitored //! path is checked (and only if it exists). //! //! Overload this to do something useful. void file_exists(string path, Stdio.Stat st);
fe76e22009-07-13Henrik Grubbström (Grubba) //! File creation callback. //! //! @param path //! Path of the new file or directory. //! //! @param st //! Status information for @[path] as obtained by @expr{file_stat(path, 1)@}. //! //! This function is called when either a monitored path has started //! existing, or when a new file or directory has been added to a //! monitored directory. //! //! Called by @[check()] and @[check_monitor()]. //! //! Overload this to do something useful. void file_created(string path, Stdio.Stat st); //! File deletion callback. //! //! @param path //! Path of the new file or directory that has been deleted. //! //! This function is called when either a monitored path has stopped //! to exist, or when a file or directory has been deleted from a //! monitored directory. //! //! Called by @[check()] and @[check_monitor()]. //! //! Overload this to do something useful. void file_deleted(string path); //! Stable change callback. //! //! @param path //! Path of the file or directory that has stopped changing. //!
7ef0192009-07-15Henrik Grubbström (Grubba) //! @param st //! Status information for @[path] as obtained by @expr{file_stat(path, 1)@}. //!
fe76e22009-07-13Henrik Grubbström (Grubba) //! This function is called when previous changes to @[path] are //! considered "stable". //! //! "Stable" in this case means that there have been no detected //! changes for at lease @[stable_time] seconds. //! //! Called by @[check()] and @[check_monitor()]. //! //! Overload this to do something useful.
7ef0192009-07-15Henrik Grubbström (Grubba) void stable_data_change(string path, Stdio.Stat st);
fe76e22009-07-13Henrik Grubbström (Grubba) 
249a3f2009-07-13Henrik Grubbström (Grubba) //! Flags for @[Monitor]s. enum MonitorFlags { MF_RECURSE = 1, MF_AUTO = 2, MF_INITED = 4, };
915a1a2009-07-22Henrik Grubbström (Grubba) //! Monitoring information for a single filesystem path. //! //! @seealso //! @[monitor()]
249a3f2009-07-13Henrik Grubbström (Grubba) protected class Monitor(string path, MonitorFlags flags,
fe76e22009-07-13Henrik Grubbström (Grubba)  int max_dir_check_interval,
f3483e2009-07-16Henrik Grubbström (Grubba)  int file_interval_factor, int stable_time)
fe76e22009-07-13Henrik Grubbström (Grubba) { int next_poll; Stdio.Stat st; int last_change = 0x7fffffff; // Future... array(string) files; int `<(mixed m) { return next_poll < m; } int `>(mixed m) { return next_poll > m; } protected string _sprintf(int c) { return sprintf("Monitor(%O, %O, next: %s, st: %O)",
249a3f2009-07-13Henrik Grubbström (Grubba)  path, flags, ctime(next_poll) - "\n", st);
fe76e22009-07-13Henrik Grubbström (Grubba)  }
915a1a2009-07-22Henrik Grubbström (Grubba) 
c51c362009-08-05Henrik Grubbström (Grubba)  //! Calculate and set a suitable time for the next poll of this monitor.
915a1a2009-07-22Henrik Grubbström (Grubba)  //! //! @param st //! New stat for the monitor. //! //! This function is called by @[check()] to schedule the //! next check. protected void update(Stdio.Stat st) { int delta = max_dir_check_interval || global::max_dir_check_interval; this_program::st = st; if (!st || !st->isdir) { delta *= file_interval_factor || global::file_interval_factor; } if (!next_poll) { // Attempt to distribute polls evenly at startup. delta = 1 + random(delta); } if (st) { int d = 1 + ((time(1) - st->mtime)>>4); if (d < 0) d = max_dir_check_interval || global::max_dir_check_interval; if (d < delta) delta = d; d = 1 + ((time(1) - st->ctime)>>4); if (d < 0) d = max_dir_check_interval || global::max_dir_check_interval; if (d < delta) delta = d; }
c51c362009-08-05Henrik Grubbström (Grubba)  if (last_change <= time(1)) { // Time until stable.
067fe62009-08-07Henrik Grubbström (Grubba)  int d = last_change + (stable_time || global::stable_time) - time(1);
c51c362009-08-05Henrik Grubbström (Grubba)  d >>= 1; if (d < 0) d = 1; if (d < delta) delta = d; }
915a1a2009-07-22Henrik Grubbström (Grubba)  next_poll = time(1) + (delta || 1); monitor_queue->adjust(this); } //! Check for changes. //! //! @param flags //! @int //! @value 0 //! Don't recurse. //! @value 1 //! Check all monitors for the entire subtree rooted in @[m]. //! @endint //! //! This function is called by @[check()] for the @[Monitor]s //! it considers need checking. If it detects any changes an //! appropriate callback will be called. //! //! @returns //! Returns @expr{1@} if a change was detected and @expr{0@} (zero) //! otherwise. //! //! @note //! Any callbacks will be called from the same thread as the one //! calling @[check_monitor()]. //! //! @note //! The return value can not be trusted to return @expr{1@} for all //! detected changes in recursive mode. //! //! @seealso //! @[check()], @[data_changed()], @[attr_changed()], @[file_created()], //! @[file_deleted()], @[stable_data_change()] int(0..1) check(MonitorFlags|void flags) { // werror("Checking monitor %O...\n", this); Stdio.Stat st = file_stat(path, 1); Stdio.Stat old_st = this_program::st; int orig_flags = this_program::flags; this_program::flags |= MF_INITED; update(st); if (!(orig_flags & MF_INITED)) { // Initialize. if (st) { if (st->isdir) {
21e5002009-10-13Henrik Grubbström (Grubba)  array(string) files = get_dir(path) || ({});
915a1a2009-07-22Henrik Grubbström (Grubba)  this_program::files = files; foreach(files, string file) { file = Stdio.append_path(path, file); if (monitors[file]) { // There's already a monitor for the file. // Assume it has already notified about existance. continue; } if (this_program::flags & MF_RECURSE) { monitor(file, orig_flags | MF_AUTO, max_dir_check_interval, file_interval_factor, stable_time); check_monitor(monitors[file]); } else if (file_exists) { file_exists(file, file_stat(file, 1)); } } } // Signal file_exists for path as an end marker. if (file_exists) { file_exists(path, st); } } return 1; } if (!st) { if (old_st) {
b0a5fc2009-10-19Henrik Grubbström (Grubba)  int delay; // Propagate deletions to any submonitors. if (files) { foreach(files, string file) { file = canonic_path(Stdio.append_path(path, file)); if (monitors[file]) { // Adjust next_poll, so that the monitor will be checked soon. monitors[file]->next_poll = time(1)-1; monitor_queue->adjust(monitors[file]); delay = 1; } }
915a1a2009-07-22Henrik Grubbström (Grubba)  }
b0a5fc2009-10-19Henrik Grubbström (Grubba)  if (delay) { // Delay the notification until the submonitors have notified. st = old_st; next_poll = time(1); monitor_queue->adjust(this); } else { if (this_program::flags & MF_AUTO) { m_delete(monitors, path); release_monitor(this); } if (file_deleted) { file_deleted(path); } return 1;
915a1a2009-07-22Henrik Grubbström (Grubba)  } } return 0; } if (!old_st) { last_change = time(1); if (file_created) { file_created(path, st); } return 1; } if ((st->mtime != old_st->mtime) || (st->ctime != old_st->ctime) || (st->size != old_st->size)) { last_change = time(1); if (st->isdir) {
21e5002009-10-13Henrik Grubbström (Grubba)  array(string) files = get_dir(path) || ({});
915a1a2009-07-22Henrik Grubbström (Grubba)  array(string) new_files = files; array(string) deleted_files = ({}); if (this_program::files) { new_files -= this_program::files; deleted_files = this_program::files - files; } this_program::files = files; foreach(new_files, string file) { file = Stdio.append_path(path, file); Monitor m2 = monitors[file]; mixed err = catch { if (m2) { // We have a separate monitor on the created file. // Let it handle the notification. m2->check(flags); } }; if (this_program::flags & MF_RECURSE) { monitor(file, orig_flags | MF_AUTO, max_dir_check_interval, file_interval_factor, stable_time); monitors[file]->check(); } else if (!m2 && file_created) { file_created(file, file_stat(file, 1)); } } foreach(deleted_files, string file) { file = Stdio.append_path(path, file); Monitor m2 = monitors[file]; mixed err = catch { if (m2) { // We have a separate monitor on the deleted file. // Let it handle the notification. m2->check(flags); } }; if (this_program::flags & MF_RECURSE) { // The monitor for the file has probably removed itself, // or the user has done it by hand, in either case we // don't need to do anything more here. } else if (!m2 && file_deleted) { file_deleted(file); } if (err) throw(err); } if (flags & MF_RECURSE) { // Check the remaining files in the directory. foreach(((files - new_files) - deleted_files), string file) { file = Stdio.append_path(path, file); Monitor m2 = monitors[file]; if (m2) { m2->check(flags); } } } if (sizeof(new_files) || sizeof(deleted_files)) return 1; } else { if (data_changed) { data_changed(path); return 1; } if (attr_changed) { attr_changed(path, st); } return 1; } } if ((flags & MF_RECURSE) && (st->isdir)) { // Check the files in the directory. foreach(files, string file) { file = Stdio.append_path(path, file); Monitor m2 = monitors[file]; if (m2) { m2->check(flags); } } } if (last_change < time(1) - (stable_time || global::stable_time)) { last_change = 0x7fffffff; if (stable_data_change) { stable_data_change(path, st); } return 1; } return 0; }
fe76e22009-07-13Henrik Grubbström (Grubba) }
d5ecf22009-07-22Henrik Grubbström (Grubba) //! Canonicalize a path. //! //! @param path //! Path to canonicalize. //! //! @returns //! The default implementation returns @expr{combine_path(path, ".")@}, //! i.e. no trailing slashes. protected string canonic_path(string path) { return combine_path(path, "."); }
fe76e22009-07-13Henrik Grubbström (Grubba) //! Mapping from monitored path to corresponding @[Monitor].
3d59782009-07-15Henrik Grubbström (Grubba) //!
d5ecf22009-07-22Henrik Grubbström (Grubba) //! The paths are normalized to @expr{canonic_path(path)@},
3d59782009-07-15Henrik Grubbström (Grubba) //! //! @note //! All filesystems are handled as if case-sensitive. This should //! not be a problem for case-insensitive filesystems as long as //! case is maintained.
fe76e22009-07-13Henrik Grubbström (Grubba) protected mapping(string:Monitor) monitors = ([]); //! Heap containing all active @[Monitor]s. //! //! The heap is sorted on @[Monitor()->next_poll]. protected ADT.Heap monitor_queue = ADT.Heap(); //! Create a new monitor. //! //! @param max_dir_check_interval //! Override of @[default_max_dir_check_interval]. //! //! @param file_interval_factor //! Override of @[default_file_interval_factor].
f3483e2009-07-16Henrik Grubbström (Grubba) //! //! @param stable_time //! Override of @[default_stable_time].
fe76e22009-07-13Henrik Grubbström (Grubba) protected void create(int|void max_dir_check_interval,
f3483e2009-07-16Henrik Grubbström (Grubba)  int|void file_interval_factor, int|void stable_time)
fe76e22009-07-13Henrik Grubbström (Grubba) { if (max_dir_check_interval > 0) { this_program::max_dir_check_interval = max_dir_check_interval; } if (file_interval_factor > 0) { this_program::file_interval_factor = file_interval_factor; }
f3483e2009-07-16Henrik Grubbström (Grubba)  if (stable_time > 0) { this_program::stable_time = stable_time; }
fe76e22009-07-13Henrik Grubbström (Grubba)  clear(); } //! Clear the set of monitored files and directories.
915a1a2009-07-22Henrik Grubbström (Grubba) //! //! @note //! Due to circular datastructures, it's recomended //! to call this function prior to discarding the object.
fe76e22009-07-13Henrik Grubbström (Grubba) void clear() { monitors = ([]); monitor_queue = ADT.Heap(); } //! Calculate a suitable time for the next poll of this monitor.
3d59782009-07-15Henrik Grubbström (Grubba) //! //! @param m //! Monitor to update. //! //! @param st //! New stat for the monitor. //! //! This function is called by @[check_monitor()] to schedule the //! next check.
fe76e22009-07-13Henrik Grubbström (Grubba) protected void update_monitor(Monitor m, Stdio.Stat st) {
915a1a2009-07-22Henrik Grubbström (Grubba)  m->update(st);
fe76e22009-07-13Henrik Grubbström (Grubba) } //! Release a single @[Monitor] from monitoring. //! //! @seealso //! @[release()] protected void release_monitor(Monitor m) { m->next_poll = -1000; monitor_queue->adjust(m); while (monitor_queue->peek() < 0) {
f6976e2009-08-11Henrik Grubbström (Grubba) #if __VERSION__ < 7.8 monitor_queue->top(); #else
fe76e22009-07-13Henrik Grubbström (Grubba)  monitor_queue->pop();
f6976e2009-08-11Henrik Grubbström (Grubba) #endif
fe76e22009-07-13Henrik Grubbström (Grubba)  } }
915a1a2009-07-22Henrik Grubbström (Grubba) //! Create a new @[Monitor] for a @[path]. //! //! This function is called by @[monitor()] to create a new @[Monitor] //! object. //! //! The default implementation just calls @[Monitor()] with the same //! arguments. //! //! @seealso //! @[monitor()] protected Monitor monitor_factory(string path, MonitorFlags|void flags, int(0..)|void max_dir_check_interval, int(0..)|void file_interval_factor, int(0..)|void stable_time) { return Monitor(path, flags, max_dir_check_interval, file_interval_factor, stable_time); }
fe76e22009-07-13Henrik Grubbström (Grubba) //! Register a @[path] for monitoring. //! //! @param path //! Path to monitor. //!
249a3f2009-07-13Henrik Grubbström (Grubba) //! @param flags
fe76e22009-07-13Henrik Grubbström (Grubba) //! @int //! @value 0 //! Don't recurse. //! @value 1 //! Monitor the entire subtree, and any directories //! or files that may appear later.
249a3f2009-07-13Henrik Grubbström (Grubba) //! @value 3 //! Monitor the entire subtree, and any directories //! or files that may appear later. Remove the monitor //! automatically when @[path] is deleted.
fe76e22009-07-13Henrik Grubbström (Grubba) //! @endint //! //! @param max_dir_check_interval //! Override of @[default_max_dir_check_interval] for this path //! or subtree. //! //! @param file_interval_factor //! Override of @[default_file_interval_factor] for this path //! or subtree. //!
f3483e2009-07-16Henrik Grubbström (Grubba) //! @param stable_time //! Override of @[default_stable_time] for this path //! or subtree. //!
fe76e22009-07-13Henrik Grubbström (Grubba) //! @seealso //! @[release()]
249a3f2009-07-13Henrik Grubbström (Grubba) void monitor(string path, MonitorFlags|void flags,
fe76e22009-07-13Henrik Grubbström (Grubba)  int(0..)|void max_dir_check_interval,
f3483e2009-07-16Henrik Grubbström (Grubba)  int(0..)|void file_interval_factor, int(0..)|void stable_time)
fe76e22009-07-13Henrik Grubbström (Grubba) {
d5ecf22009-07-22Henrik Grubbström (Grubba)  path = canonic_path(path);
52367a2009-07-14Henrik Grubbström (Grubba)  Monitor m = monitors[path]; if (m) { if (!(flags & MF_AUTO)) { // The new monitor is added by hand. // Adjust the monitor. m->flags = flags; m->max_dir_check_interval = max_dir_check_interval; m->file_interval_factor = file_interval_factor;
f3483e2009-07-16Henrik Grubbström (Grubba)  m->stable_time = stable_time;
52367a2009-07-14Henrik Grubbström (Grubba)  m->next_poll = 0; monitor_queue->adjust(m); } // For the other cases there's no need to do anything, // since we can keep the monitor as-is. } else {
915a1a2009-07-22Henrik Grubbström (Grubba)  m = monitor_factory(path, flags, max_dir_check_interval, file_interval_factor, stable_time);
52367a2009-07-14Henrik Grubbström (Grubba)  monitors[path] = m; monitor_queue->push(m); }
fe76e22009-07-13Henrik Grubbström (Grubba) } //! Release a @[path] from monitoring. //! //! @param path //! Path to stop monitoring. //!
249a3f2009-07-13Henrik Grubbström (Grubba) //! @param flags
fe76e22009-07-13Henrik Grubbström (Grubba) //! @int //! @value 0 //! Don't recurse. //! @value 1 //! Release the entire subtree.
249a3f2009-07-13Henrik Grubbström (Grubba) //! @value 3 //! Release the entire subtree, but only those paths that were //! added automatically by a recursive monitor.
fe76e22009-07-13Henrik Grubbström (Grubba) //! @endint //! //! @seealso //! @[monitor()]
249a3f2009-07-13Henrik Grubbström (Grubba) void release(string path, MonitorFlags|void flags)
fe76e22009-07-13Henrik Grubbström (Grubba) {
d5ecf22009-07-22Henrik Grubbström (Grubba)  path = canonic_path(path);
fe76e22009-07-13Henrik Grubbström (Grubba)  Monitor m = m_delete(monitors, path); if (m) { release_monitor(m); }
249a3f2009-07-13Henrik Grubbström (Grubba)  if (flags && m->st && m->st->isdir) {
d5ecf22009-07-22Henrik Grubbström (Grubba)  if (!sizeof(path) || path[-1] != '/') { path += "/"; }
fe76e22009-07-13Henrik Grubbström (Grubba)  foreach(monitors; string mpath; m) {
249a3f2009-07-13Henrik Grubbström (Grubba)  if (has_prefix(mpath, path) && ((m->flags & flags) == flags)) {
fe76e22009-07-13Henrik Grubbström (Grubba)  m_delete(monitors, mpath); release_monitor(m); } } } }
9fc4072009-09-21Henrik Grubbström (Grubba) //! Check whether a path is monitored or not.
136b3b2009-09-21Henrik Grubbström (Grubba) //! //! @param path
9fc4072009-09-21Henrik Grubbström (Grubba) //! Path to check. //! //! @returns //! Returns @expr{1@} if there is a monitor on @[path], //! and @expr{0@} (zero) otherwise. //! //! @seealso //! @[monitor()], @[release()]
136b3b2009-09-21Henrik Grubbström (Grubba) int(0..1) is_monitored(string path) { return !!monitors[canonic_path(path)]; }
fe76e22009-07-13Henrik Grubbström (Grubba) //! Check a single @[Monitor] for changes. //! //! @param m //! @[Monitor] to check. //!
80f66e2009-07-14Henrik Grubbström (Grubba) //! @param flags //! @int //! @value 0 //! Don't recurse. //! @value 1 //! Check all monitors for the entire subtree rooted in @[m]. //! @endint //!
fe76e22009-07-13Henrik Grubbström (Grubba) //! This function is called by @[check()] for the @[Monitor]s //! it considers need checking. If it detects any changes an //! appropriate callback will be called. //! //! @returns //! Returns @expr{1@} if a change was detected and @expr{0@} (zero) //! otherwise. //! //! @note //! Any callbacks will be called from the same thread as the one //! calling @[check_monitor()]. //!
80f66e2009-07-14Henrik Grubbström (Grubba) //! @note //! The return value can not be trusted to return @expr{1@} for all //! detected changes in recursive mode. //!
fe76e22009-07-13Henrik Grubbström (Grubba) //! @seealso //! @[check()], @[data_changed()], @[attr_changed()], @[file_created()], //! @[file_deleted()], @[stable_data_change()]
80f66e2009-07-14Henrik Grubbström (Grubba) protected int(0..1) check_monitor(Monitor m, MonitorFlags|void flags)
fe76e22009-07-13Henrik Grubbström (Grubba) {
915a1a2009-07-22Henrik Grubbström (Grubba)  return m->check(flags);
fe76e22009-07-13Henrik Grubbström (Grubba) } //! Check for changes. //! //! @param max_wait
fb4faa2009-08-04Henrik Grubbström (Grubba) //! Maximum time in seconds to wait for changes. @expr{-1@}
fe76e22009-07-13Henrik Grubbström (Grubba) //! for infinite wait. //!
010d1c2009-08-06Henrik Grubbström (Grubba) //! @param max_cnt //! Maximum number of paths to check in this call. @expr{0@} //! (zero) for unlimited. //!
1819432009-10-12Henrik Grubbström (Grubba) //! @param ret_stats //! Optional mapping that will be filled with statistics (see below). //!
fe76e22009-07-13Henrik Grubbström (Grubba) //! A suitable subset of the monitored files will be checked //! for changes. //! //! @returns //! The function returns when either a change has been detected
fb4faa2009-08-04Henrik Grubbström (Grubba) //! or when @[max_wait] has expired. The returned value indicates //! the number of seconds until the next call of @[check()].
fe76e22009-07-13Henrik Grubbström (Grubba) //!
1819432009-10-12Henrik Grubbström (Grubba) //! If @[ret_stats] has been provided, it will be filled with //! the following entries: //! @mapping //! @member int "num_monitors" //! The total number of active monitors when the scan completed. //! @member int "scanned_monitors" //! The number of monitors that were scanned for updates during the call. //! @member int "updated_monitors" //! The number of monitors that were updated during the call. //! @member int "idle_time" //! The number of seconds that the call slept. //! @endmapping //!
fe76e22009-07-13Henrik Grubbström (Grubba) //! @note //! Any callbacks will be called from the same thread as the one //! calling @[check()]. //! //! @seealso //! @[monitor()]
1819432009-10-12Henrik Grubbström (Grubba) int check(int|void max_wait, int|void max_cnt, mapping(string:int)|void ret_stats)
fe76e22009-07-13Henrik Grubbström (Grubba) {
1819432009-10-12Henrik Grubbström (Grubba)  int scan_cnt = max_cnt; int scan_wait = max_wait;
fe76e22009-07-13Henrik Grubbström (Grubba)  while(1) {
fb4faa2009-08-04Henrik Grubbström (Grubba)  int ret = max_dir_check_interval;
fe76e22009-07-13Henrik Grubbström (Grubba)  int cnt; int t = time();
3d59782009-07-15Henrik Grubbström (Grubba)  if (sizeof(monitors)) {
21e5002009-10-13Henrik Grubbström (Grubba)  Monitor m; while ((m = monitor_queue->peek()) && (m->next_poll <= t)) {
3d59782009-07-15Henrik Grubbström (Grubba)  cnt += check_monitor(m);
21e5002009-10-13Henrik Grubbström (Grubba)  if (!(--scan_cnt)) { m = monitor_queue->peek(); break; }
3d59782009-07-15Henrik Grubbström (Grubba)  }
fb4faa2009-08-04Henrik Grubbström (Grubba)  if (m) { ret = m->next_poll - t; if (ret <= 0) ret = 1;
010d1c2009-08-06Henrik Grubbström (Grubba)  } else {
1819432009-10-12Henrik Grubbström (Grubba)  scan_cnt--; } } if (cnt || !scan_wait || !scan_cnt) { if (ret_stats) { ret_stats->num_monitors = sizeof(monitors); ret_stats->scanned_monitors = max_cnt - scan_cnt; ret_stats->updated_monitors = cnt; ret_stats->idle_time = max_wait - scan_wait;
fb4faa2009-08-04Henrik Grubbström (Grubba)  }
1819432009-10-12Henrik Grubbström (Grubba)  return ret;
fb4faa2009-08-04Henrik Grubbström (Grubba)  }
1819432009-10-12Henrik Grubbström (Grubba)  if (ret < scan_wait) { scan_wait -= ret;
fb4faa2009-08-04Henrik Grubbström (Grubba)  sleep(ret); } else {
1819432009-10-12Henrik Grubbström (Grubba)  if (scan_wait > 0) scan_wait--;
fb4faa2009-08-04Henrik Grubbström (Grubba)  sleep(1);
fe76e22009-07-13Henrik Grubbström (Grubba)  } } } //! Backend to use.
90569b2009-07-13Henrik Grubbström (Grubba) //! //! If @expr{0@} (zero) - use the default backend.
fe76e22009-07-13Henrik Grubbström (Grubba) protected Pike.Backend backend; //! Call-out identifier for @[backend_check()] if in //! nonblocking mode. //! //! @seealso //! @[set_nonblocking()], @[set_blocking()] protected mixed co_id; //! Change backend. //! //! @param backend //! Backend to use. @expr{0@} (zero) for the default backend. void set_backend(Pike.Backend|void backend) { int was_nonblocking = !!co_id; set_blocking(); this_program::backend = backend; if (was_nonblocking) { set_nonblocking(); } }
90569b2009-07-13Henrik Grubbström (Grubba) //! Turn off nonblocking mode. //! //! @seealso //! @[set_nonblocking()]
fe76e22009-07-13Henrik Grubbström (Grubba) void set_blocking() { if (co_id) { if (backend) backend->remove_call_out(co_id); else remove_call_out(co_id); co_id = 0; } }
90569b2009-07-13Henrik Grubbström (Grubba) //! Backend check callback function. //! //! This function is intended to be called from a backend, //! and performs a @[check()] followed by rescheduling //! itself via a call to @[set_nonblocking()]. //! //! @seealso //! @[check()], @[set_nonblocking()]
fe76e22009-07-13Henrik Grubbström (Grubba) protected void backend_check() { co_id = 0;
fb4faa2009-08-04Henrik Grubbström (Grubba)  int t;
fe76e22009-07-13Henrik Grubbström (Grubba)  mixed err = catch {
fb4faa2009-08-04Henrik Grubbström (Grubba)  t = check(0);
fe76e22009-07-13Henrik Grubbström (Grubba)  };
fb4faa2009-08-04Henrik Grubbström (Grubba)  set_nonblocking(t);
fe76e22009-07-13Henrik Grubbström (Grubba)  if (err) throw(err); }
90569b2009-07-13Henrik Grubbström (Grubba) //! Turn on nonblocking mode. //!
fb4faa2009-08-04Henrik Grubbström (Grubba) //! @param t //! Suggested time in seconds until next call of @[check()]. //!
90569b2009-07-13Henrik Grubbström (Grubba) //! Register suitable callbacks with the backend to automatically //! call @[check()]. //! //! @[check()] and thus all the callbacks will be called from the //! backend thread. //!
fb4faa2009-08-04Henrik Grubbström (Grubba) //! @note //! If nonblocking mode is already active, this function will //! be a noop. //!
90569b2009-07-13Henrik Grubbström (Grubba) //! @seealso //! @[set_blocking()], @[check()].
fb4faa2009-08-04Henrik Grubbström (Grubba) void set_nonblocking(int|void t)
fe76e22009-07-13Henrik Grubbström (Grubba) { if (co_id) return;
fb4faa2009-08-04Henrik Grubbström (Grubba)  if (!zero_type(t)) { Monitor m = monitor_queue->peek(); t = (m && m->next_poll - time(1)) || max_dir_check_interval; if (t > max_dir_check_interval) t = max_dir_check_interval; if (t < 0) t = 0; }
fe76e22009-07-13Henrik Grubbström (Grubba)  if (backend) co_id = backend->call_out(backend_check, t); else co_id = call_out(backend_check, t); }
d951102009-07-17Henrik Grubbström (Grubba)  //! Set the @[default_max_dir_check_interval]. void set_max_dir_check_interval(int max_dir_check_interval) { if (max_dir_check_interval > 0) { this_program::max_dir_check_interval = max_dir_check_interval; } else { this_program::max_dir_check_interval = default_max_dir_check_interval; } } //! Set the @[default_file_interval_factor]. void set_file_interval_factor(int file_interval_factor) { if (file_interval_factor > 0) { this_program::file_interval_factor = file_interval_factor; } else { this_program::file_interval_factor = default_file_interval_factor; } }