B
    \2                 @   sD  d Z ddlZddlZddlZddlZddlZddlZddlZddlm	Z	 ddl
mZmZmZ ddlmZ ddlmZ ddlmZ ddlmZ dd	lmZ ddlZdd
lmZmZmZmZ ejrddlmZ ejZe dddZ!ddddZ"ee e f dddZ#da$dee  e e dddZ%ee  dddZ&G dd de'Z(dS )zUtilities for working with multiple processes, including both forking
the server into multiple processes and managing subprocesses.
    N)hexlify)Future"future_set_result_unless_cancelled%future_set_exception_unless_cancelled)ioloop)PipeIOStream)gen_log)set_close_exec)errno_from_exception)TupleOptionalAnyCallable)List)returnc            	   C   s\   t dkrdS yt  S  tk
r(   Y nX y
tdS  ttfk
rL   Y nX td dS )z1Returns the number of processors on this machine.N   SC_NPROCESSORS_CONFz1Could not detect number of processors; assuming 1)	multiprocessing	cpu_countNotImplementedErrorossysconfAttributeError
ValueErrorr   error r   r   .lib/python3.7/site-packages/tornado/process.pyr   3   s    

r   c              C   sj   dt jkrd S dd l} ytttdd}W n, tk
rZ   tt d t	 A }Y nX | 
| d S )Nrandomr      i  )sysmodulesr   intr   r   urandomr   timegetpidseed)r   r%   r   r   r   _reseed_randomC   s    
r&   c              C   s$   t  \} }t|  t| | |fS )N)r   piper	   )rwr   r   r   _pipe_cloexecR   s    r*   )num_processesmax_restartsr   c       
   
      s~  |dkrd}t dkst| dks(| dkr.t } td|  i  ttt d fdd}x$t| D ]}||}|dk	r`|S q`W d}x rnyt	 \}}W n4 t
k
r } zt|tjkrw W dd}~X Y nX | krq |}t|rtd||t| n8t|dkr.td	||t| ntd
|| q|d7 }||krXtd||}	|	dk	r|	S qW td dS )a  Starts multiple worker processes.

    If ``num_processes`` is None or <= 0, we detect the number of cores
    available on this machine and fork that number of child
    processes. If ``num_processes`` is given and > 0, we fork that
    specific number of sub-processes.

    Since we use processes and not threads, there is no shared memory
    between any server code.

    Note that multiple processes are not compatible with the autoreload
    module (or the ``autoreload=True`` option to `tornado.web.Application`
    which defaults to True when ``debug=True``).
    When using multiple processes, no IOLoops can be created or
    referenced until after the call to ``fork_processes``.

    In each child process, ``fork_processes`` returns its *task id*, a
    number between 0 and ``num_processes``.  Processes that exit
    abnormally (due to a signal or non-zero exit status) are restarted
    with the same id (up to ``max_restarts`` times).  In the parent
    process, ``fork_processes`` returns None if all child processes
    have exited normally, but will otherwise only exit by throwing an
    exception.

    max_restarts defaults to 100.
    Nd   r   zStarting %d processes)ir   c                s.   t  }|dkrt  | a| S |  |< d S d S )Nr   )r   forkr&   _task_id)r.   pid)childrenr   r   start_child   s    z#fork_processes.<locals>.start_childz1child %d (pid %d) killed by signal %d, restartingz3child %d (pid %d) exited with status %d, restartingz!child %d (pid %d) exited normallyr   z"Too many child restarts, giving up)r0   AssertionErrorr   r   infor!   r   ranger   waitOSErrorr
   errnoZEINTRpopWIFSIGNALEDZwarningWTERMSIGWEXITSTATUSRuntimeErrorr   exit)
r+   r,   r3   r.   idZnum_restartsr1   statuseZnew_idr   )r2   r   fork_processes\   sX    

rC   c               C   s   t S )zpReturns the current task id, if any.

    Returns None if this process was not created by `fork_processes`.
    )r0   r   r   r   r   task_id   s    rD   c               @   s   e Zd ZdZe ZdZi ZdZe	e	ddddZ
eegdf dddd	ZdeddddZeddddZeddddZeddddZeeddddZeddddZdS )
Subprocessa   Wraps ``subprocess.Popen`` with IOStream support.

    The constructor is the same as ``subprocess.Popen`` with the following
    additions:

    * ``stdin``, ``stdout``, and ``stderr`` may have the value
      ``tornado.process.Subprocess.STREAM``, which will make the corresponding
      attribute of the resulting Subprocess a `.PipeIOStream`. If this option
      is used, the caller is responsible for closing the streams when done
      with them.

    The ``Subprocess.STREAM`` option and the ``set_exit_callback`` and
    ``wait_for_exit`` methods do not work on Windows. There is
    therefore no reason to use this class instead of
    ``subprocess.Popen`` on that platform.

    .. versionchanged:: 5.0
       The ``io_loop`` argument (deprecated since version 4.1) has been removed.

    FN)argskwargsr   c             O   s  t j | _g }g }|dtjkrXt \}}||d< |||f |	| t
|| _|dtjkrt \}}||d< |||f |	| t
|| _|dtjkrt \}	}
|
|d< ||	|
f |	|
 t
|	| _ytj||| _W n(   x|D ]}t| q W  Y nX x|D ]}t| q"W | jj| _x.dD ]&}t| |sFt| |t| j| qFW d | _d | _d S )Nstdinstdoutstderr)rH   rI   rJ   )r   IOLoopcurrentio_loopgetrE   STREAMr*   extendappendr   rH   rI   rJ   
subprocessPopenprocr   closer1   hasattrsetattrgetattr_exit_callback
returncode)selfrF   rG   Zpipe_fdsZto_closeZin_rZin_wZout_rZout_wZerr_rZerr_wfdattrr   r   r   __init__   sF    












zSubprocess.__init__)callbackr   c             C   s*   || _ t  | tj| j< t| j dS )a  Runs ``callback`` when this process exits.

        The callback takes one argument, the return code of the process.

        This method uses a ``SIGCHLD`` handler, which is a global setting
        and may conflict if you have other libraries trying to handle the
        same signal.  If you are using more than one ``IOLoop`` it may
        be necessary to call `Subprocess.initialize` first to designate
        one ``IOLoop`` to run the signal handlers.

        In many cases a close callback on the stdout or stderr streams
        can be used as an alternative to an exit callback if the
        signal handler is causing a problem.
        N)rY   rE   
initialize_waitingr1   _try_cleanup_process)r[   r_   r   r   r   set_exit_callback  s    zSubprocess.set_exit_callbackTzFuture[int])raise_errorr   c                s*   t   tdd fdd}| |  S )a  Returns a `.Future` which resolves when the process exits.

        Usage::

            ret = yield proc.wait_for_exit()

        This is a coroutine-friendly alternative to `set_exit_callback`
        (and a replacement for the blocking `subprocess.Popen.wait`).

        By default, raises `subprocess.CalledProcessError` if the process
        has a non-zero exit status. Use ``wait_for_exit(raise_error=False)``
        to suppress this behavior and return the exit status without raising.

        .. versionadded:: 4.2
        N)retr   c                s,   | dkrrt  t| d n
t |  d S )Nr   unknown)r   CalledProcessErrorr   )re   )futurerd   r   r   r_   ,  s    z*Subprocess.wait_for_exit.<locals>.callback)r   r!   rc   )r[   rd   r_   r   )rh   rd   r   wait_for_exit  s    	
zSubprocess.wait_for_exit)r   c                s8    j r
dS tj ttj fdd _d _ dS )a  Initializes the ``SIGCHLD`` handler.

        The signal handler is run on an `.IOLoop` to avoid locking issues.
        Note that the `.IOLoop` used for signal handling need not be the
        same one used by individual Subprocess objects (as long as the
        ``IOLoops`` are each running in separate threads).

        .. versionchanged:: 5.0
           The ``io_loop`` argument (deprecated since version 4.1) has been
           removed.
        Nc                s     jS )N)add_callback_from_signal_cleanup)Zsigframe)clsrM   r   r   <lambda>J  s    z'Subprocess.initialize.<locals>.<lambda>T)_initializedr   rK   rL   signalSIGCHLD_old_sigchld)rm   r   )rm   rM   r   r`   8  s    
zSubprocess.initializec             C   s$   | j s
dS ttj| j d| _ dS )z Removes the ``SIGCHLD`` handler.NF)ro   rp   rq   rr   )rm   r   r   r   uninitializeN  s    zSubprocess.uninitializec             C   s&   x t | j D ]}| | qW d S )N)listra   keysrb   )rm   r1   r   r   r   rk   V  s    zSubprocess._cleanup)r1   r   c          
   C   s   yt |t j\}}W n4 tk
rJ } zt|tjkr:d S W d d }~X Y nX |dkrXd S ||ksdt| j	|}|j
|j| d S )Nr   )r   waitpidWNOHANGr8   r
   r9   ZECHILDr4   ra   r:   rM   rj   _set_returncode)rm   r1   Zret_pidrA   rB   Zsubprocr   r   r   rb   [  s    zSubprocess._try_cleanup_process)rA   r   c             C   s^   t |rt | | _nt |s(tt || _| j| j_| jrZ| j}d | _|| j d S )N)	r   r;   r<   rZ   	WIFEXITEDr4   r=   rT   rY   )r[   rA   r_   r   r   r   rx   h  s    

zSubprocess._set_returncode)T)__name__
__module____qualname____doc__objectrO   ro   ra   rr   r   r^   r   r!   rc   boolri   classmethodr`   rs   rk   rb   rx   r   r   r   r   rE      s"   'rE   )N))r}   r9   r   r   rp   rR   r   r#   Zbinasciir   Ztornado.concurrentr   r   r   Ztornador   Ztornado.iostreamr   Ztornado.logr   Ztornado.platform.autor	   Ztornado.utilr
   typingr   r   r   r   ZTYPE_CHECKINGr   rg   r!   r   r&   r*   r0   rC   rD   r~   rE   r   r   r   r   <module>   s4   ^	