docstr: Spawn an upload job, and handles the bookkeeping of `self._running_jobs`.
  
  Context: it's important that, whenever we add an entry to `self._running_jobs`,
  we ensure that a corresponding `EventJobDone` message will eventually get handled;
  otherwise, the `_running_jobs` entry will never get removed, and the StepUpload
  will never shut down.
  
  The sole purpose of this function is to make sure that the code that adds an entry
  to `self._running_jobs` is textually right next to the code that eventually enqueues
  the `EventJobDone` message. This should help keep them in sync.
docstr_tok: ["Spawn","an","upload","job",",","and","handles","the","bookkeeping","of","`","self._running_jobs","`",".","Context",":","it","'s","important","that",",","whenever","we","add","an","entry","to","`","self._running_jobs","`",",","we","ensure","that","a","corresponding","`","EventJobDone","`","message","will","eventually","get","handled",";","otherwise",",","the","`","_running_jobs","`","entry","will","never","get","removed",",","and","the","StepUpload","will","never","shut","down",".","The","sole","purpose","of","this","function","is","to","make","sure","that","the","code","that","adds","an","entry","to","`","self._running_jobs","`","is","textually","right","next","to","the","code","that","eventually","enqueues","the","`","EventJobDone","`","message",".","This","should","help","keep","them","in","sync","."]
code: def _spawn_upload_sync(self, event: RequestUpload) -> None:
      """Spawn an upload job, and handles the bookkeeping of `self._running_jobs`.
  
      Context: it's important that, whenever we add an entry to `self._running_jobs`,
      we ensure that a corresponding `EventJobDone` message will eventually get handled;
      otherwise, the `_running_jobs` entry will never get removed, and the StepUpload
      will never shut down.
  
      The sole purpose of this function is to make sure that the code that adds an entry
      to `self._running_jobs` is textually right next to the code that eventually enqueues
      the `EventJobDone` message. This should help keep them in sync.
      """
      # Adding the entry to `self._running_jobs` MUST happen in the main thread,
      # NOT in the job that gets submitted to the thread-pool, to guard against
      # this sequence of events:
      # - StepUpload receives a RequestUpload
      #     ...and therefore spawns a thread to do the upload
      # - StepUpload receives a RequestFinish
      #     ...and checks `self._running_jobs` to see if there are any tasks to wait for...
      #     ...and there are none, because the addition to `self._running_jobs` happens in
      #        the background thread, which the scheduler hasn't yet run...
      #     ...so the StepUpload shuts down. Even though we haven't uploaded the file!
      #
      # This would be very bad!
      # So, this line has to happen _outside_ the `pool.submit()`.
      self._running_jobs[event.save_name] = event
  
      def run_and_notify() -> None:
          try:
              self._do_upload_sync(event)
          finally:
              self._event_queue.put(
                  EventJobDone(event, exc=sys.exc_info()[1])
              )
  
      self._pool.submit(run_and_notify)
  
imports: import asyncio
  import concurrent.futures
  import logging
  import queue
  import sys
  import threading
  from typing import TYPE_CHECKING, Awaitable, Callable, MutableMapping, MutableSequence, MutableSet, NamedTuple, Optional, Union
  from wandb.errors.term import termerror
  from wandb.filesync import upload_job
  from wandb.sdk.lib.paths import LogicalPath
label_desc: Use image node_id 5 for calling the StepUpload obj's underlying member method code with example usage: obj._spawn_upload_sync(event) without return types