|  | @@ -17,6 +17,8 @@ import time
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  from collections import defaultdict
 |  |  from collections import defaultdict
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +from kombu.utils import eventio
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  from celery.platforms import ignore_EBADF
 |  |  from celery.platforms import ignore_EBADF
 | 
											
												
													
														|  |  from celery.utils.imports import module_file
 |  |  from celery.utils.imports import module_file
 | 
											
												
													
														|  |  from celery.utils.log import get_logger
 |  |  from celery.utils.log import get_logger
 | 
											
										
											
												
													
														|  | @@ -42,11 +44,20 @@ class WorkerComponent(StartStopComponent):
 | 
											
												
													
														|  |          self.enabled = w.autoreload = autoreload
 |  |          self.enabled = w.autoreload = autoreload
 | 
											
												
													
														|  |          w.autoreloader = None
 |  |          w.autoreloader = None
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    def create(self, w):
 |  | 
 | 
											
												
													
														|  | -        w.autoreloader = self.instantiate(w.autoreloader_cls,
 |  | 
 | 
											
												
													
														|  | -                                          controller=w)
 |  | 
 | 
											
												
													
														|  | 
 |  | +    def create_ev(self, w):
 | 
											
												
													
														|  | 
 |  | +        ar = w.autoreloader = self.instantiate(w.autoreloader_cls, w)
 | 
											
												
													
														|  | 
 |  | +        w.hub.on_init.append(ar.on_poll_init)
 | 
											
												
													
														|  | 
 |  | +        w.hub.on_close.append(ar.on_poll_close)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def create_threaded(self, w):
 | 
											
												
													
														|  | 
 |  | +        w.autoreloader = self.instantiate(w.autoreloader_cls, w)
 | 
											
												
													
														|  |          return w.autoreloader
 |  |          return w.autoreloader
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +    def create(self, w):
 | 
											
												
													
														|  | 
 |  | +        if hasattr(select, "kqueue") and w.use_eventloop:
 | 
											
												
													
														|  | 
 |  | +            return self.create_ev(w)
 | 
											
												
													
														|  | 
 |  | +        return self.create_threaded(w)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  def file_hash(filename, algorithm="md5"):
 |  |  def file_hash(filename, algorithm="md5"):
 | 
											
												
													
														|  |      hobj = hashlib.new(algorithm)
 |  |      hobj = hashlib.new(algorithm)
 | 
											
										
											
												
													
														|  | @@ -107,40 +118,46 @@ class KQueueMonitor(BaseMonitor):
 | 
											
												
													
														|  |      """File change monitor based on BSD kernel event notifications"""
 |  |      """File change monitor based on BSD kernel event notifications"""
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      def __init__(self, *args, **kwargs):
 |  |      def __init__(self, *args, **kwargs):
 | 
											
												
													
														|  | -        assert hasattr(select, "kqueue")
 |  | 
 | 
											
												
													
														|  |          super(KQueueMonitor, self).__init__(*args, **kwargs)
 |  |          super(KQueueMonitor, self).__init__(*args, **kwargs)
 | 
											
												
													
														|  |          self.filemap = dict((f, None) for f in self.files)
 |  |          self.filemap = dict((f, None) for f in self.files)
 | 
											
												
													
														|  | 
 |  | +        self.fdmap = {}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    def start(self):
 |  | 
 | 
											
												
													
														|  | -        self._kq = select.kqueue()
 |  | 
 | 
											
												
													
														|  | -        kevents = []
 |  | 
 | 
											
												
													
														|  | 
 |  | +    def on_poll_init(self, hub):
 | 
											
												
													
														|  | 
 |  | +        self.add_events(hub.poller)
 | 
											
												
													
														|  | 
 |  | +        hub.poller.on_file_change = self.handle_event
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def on_poll_close(self, hub):
 | 
											
												
													
														|  | 
 |  | +        self.close(hub.poller)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def add_events(self, poller):
 | 
											
												
													
														|  |          for f in self.filemap:
 |  |          for f in self.filemap:
 | 
											
												
													
														|  |              self.filemap[f] = fd = os.open(f, os.O_RDONLY)
 |  |              self.filemap[f] = fd = os.open(f, os.O_RDONLY)
 | 
											
												
													
														|  | 
 |  | +            self.fdmap[fd] = f
 | 
											
												
													
														|  | 
 |  | +            poller.watch_file(fd)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -            ev = select.kevent(fd,
 |  | 
 | 
											
												
													
														|  | -                    filter=select.KQ_FILTER_VNODE,
 |  | 
 | 
											
												
													
														|  | -                    flags=select.KQ_EV_ADD |
 |  | 
 | 
											
												
													
														|  | -                            select.KQ_EV_ENABLE |
 |  | 
 | 
											
												
													
														|  | -                            select.KQ_EV_CLEAR,
 |  | 
 | 
											
												
													
														|  | -                    fflags=select.KQ_NOTE_WRITE |
 |  | 
 | 
											
												
													
														|  | -                            select.KQ_NOTE_EXTEND)
 |  | 
 | 
											
												
													
														|  | -            kevents.append(ev)
 |  | 
 | 
											
												
													
														|  | 
 |  | +    def handle_event(self, events):
 | 
											
												
													
														|  | 
 |  | +        self.on_change([self.fdmap[e.ident] for e in events])
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        events = self._kq.control(kevents, 0)
 |  | 
 | 
											
												
													
														|  | 
 |  | +    def start(self):
 | 
											
												
													
														|  | 
 |  | +        self.poller = eventio.poll()
 | 
											
												
													
														|  | 
 |  | +        self.add_events(self.poller._kcontrol)
 | 
											
												
													
														|  | 
 |  | +        self.poller.on_file_change = self.handle_event
 | 
											
												
													
														|  |          while not self.shutdown_event.is_set():
 |  |          while not self.shutdown_event.is_set():
 | 
											
												
													
														|  | -            events = self._kq.control(kevents, 1)
 |  | 
 | 
											
												
													
														|  | -            fds = [e.ident for e in events]
 |  | 
 | 
											
												
													
														|  | -            modified = [k for k, v in self.filemap.iteritems()
 |  | 
 | 
											
												
													
														|  | -                                        if v in fds]
 |  | 
 | 
											
												
													
														|  | -            self.on_change(modified)
 |  | 
 | 
											
												
													
														|  | 
 |  | +            self.poller.poll(1)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    def stop(self):
 |  | 
 | 
											
												
													
														|  | -        self._kq.close()
 |  | 
 | 
											
												
													
														|  | -        for fd in filter(None, self.filemap.values()):
 |  | 
 | 
											
												
													
														|  | 
 |  | +    def close(self, poller):
 | 
											
												
													
														|  | 
 |  | +        for f, fd in filter(None, self.filemap.iteritems()):
 | 
											
												
													
														|  | 
 |  | +            poller.unregister(fd)
 | 
											
												
													
														|  |              with ignore_EBADF():  # pragma: no cover
 |  |              with ignore_EBADF():  # pragma: no cover
 | 
											
												
													
														|  |                  os.close(fd)
 |  |                  os.close(fd)
 | 
											
												
													
														|  | -            self.filemap[fd] = None
 |  | 
 | 
											
												
													
														|  | 
 |  | +            self.filemap.pop(f, None)
 | 
											
												
													
														|  | 
 |  | +            self.fdmap.pop(fd, None)
 | 
											
												
													
														|  |          self.filemap.clear()
 |  |          self.filemap.clear()
 | 
											
												
													
														|  | 
 |  | +        self.fdmap.clear()
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def stop(self):
 | 
											
												
													
														|  | 
 |  | +        self._kq.close()
 | 
											
												
													
														|  | 
 |  | +        self.close(self.poller)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  class InotifyMonitor(_ProcessEvent):
 |  |  class InotifyMonitor(_ProcessEvent):
 | 
											
										
											
												
													
														|  | @@ -207,15 +224,26 @@ class Autoreloader(bgThread):
 | 
											
												
													
														|  |          app = self.controller.app
 |  |          app = self.controller.app
 | 
											
												
													
														|  |          self.modules = app.loader.task_modules if modules is None else modules
 |  |          self.modules = app.loader.task_modules if modules is None else modules
 | 
											
												
													
														|  |          self.options = options
 |  |          self.options = options
 | 
											
												
													
														|  | -        self.Monitor = monitor_cls or self.Monitor
 |  | 
 | 
											
												
													
														|  |          self._monitor = None
 |  |          self._monitor = None
 | 
											
												
													
														|  |          self._hashes = None
 |  |          self._hashes = None
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    def body(self):
 |  | 
 | 
											
												
													
														|  | 
 |  | +    def on_init(self):
 | 
											
												
													
														|  |          files = [module_file(sys.modules[m]) for m in self.modules]
 |  |          files = [module_file(sys.modules[m]) for m in self.modules]
 | 
											
												
													
														|  | 
 |  | +        self._hashes = dict((f, file_hash(f)) for f in files)
 | 
											
												
													
														|  |          self._monitor = self.Monitor(files, self.on_change,
 |  |          self._monitor = self.Monitor(files, self.on_change,
 | 
											
												
													
														|  |                  shutdown_event=self._is_shutdown, **self.options)
 |  |                  shutdown_event=self._is_shutdown, **self.options)
 | 
											
												
													
														|  | -        self._hashes = dict([(f, file_hash(f)) for f in files])
 |  | 
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def on_poll_init(self, hub):
 | 
											
												
													
														|  | 
 |  | +        if self._monitor is None:
 | 
											
												
													
														|  | 
 |  | +            self.on_init()
 | 
											
												
													
														|  | 
 |  | +        self._monitor.on_poll_init(hub)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def on_poll_close(self, hub):
 | 
											
												
													
														|  | 
 |  | +        if self._monitor is not None:
 | 
											
												
													
														|  | 
 |  | +            self._monitor.on_poll_close(hub)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def body(self):
 | 
											
												
													
														|  | 
 |  | +        self.on_init()
 | 
											
												
													
														|  |          try:
 |  |          try:
 | 
											
												
													
														|  |              self._monitor.start()
 |  |              self._monitor.start()
 | 
											
												
													
														|  |          except OSError, exc:
 |  |          except OSError, exc:
 |