TODO:

- Print error when tmp directory can't be deleted (in instant-clean).
  Maybe add username to tmp dir name suffix?
  /tmp on cluster contains instant directories 
  of many users.

- Keep a most recently used list
  add a max cache size option to ~/.instant/intantrc
  clean up the oldest modules whenever we exceed this quota

- Add argument to provide a cache subfolder name.
  Then instant-clean can take an argument to clean only the "ffc" cache subfolder etc.

- Does instant handle full disk properly? (Does any software? Define properly!)

- Add file locking for the cache system, to avoid problems
  when multiple processes work with the same module.
  On UNIX, something like this:

import os.path
import fcntl
def get_lock(cache_dir, module_name):
    lock = open(os.path.join(cache_dir, module_name + ".lock"), "w")
    fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
    return lock

def release_lock(lock):
    fcntl.flock(lock.fileno(), fcntl.LOCK_UN)
    lock.close()
  
  windows support (no fcntl) can be restricted to "do nothing", e.g. def get_lock(*args): pass; def release_lock(*args): pass

  The lock must be held during two operations:
  1) Get lock, check if the module exists, release lock, eventually start compilation in /tmp
  2) Get lock, check if the module exists, _otherwise_ copy the finished compiled module from /tmp/foo to the cache directory, release lock
  Note that this way, two processes may both
  start compiling in different tmp dirs, and
  the one that finishes first gets to copy
  into the cache. If one is interrupted during
  compilation, the other doesn't care.
  If one is interrupted while holding the lock,
  we have a problem. But at least the lock is
  only held for very short periods of time.

- Fix arguments not used in setup.py

- Remove unused csrcs stuff.

- Explain arrays argument.

- Use object files argument.

- Improve documentation

- Add tests for all variants of cache mechanisms available now

- Clean up imports, don't use import *


Explain this in manual:

    Use cases: (M - modulename, S - signature, C - enable_cache == True)
    - 'MS'  - Invalid
    
    - 'M'   - Use given M and no cache, simply compile in current directory.
              import_module(M) will work in the current directory, but always
              using the version compiled first in this python process, because
              of limitations in the Python extension module system.
              The user can't change flags during the current python process lifetime!

                use_cache = False
                moduleids = []
                
                module_path = cwd
                modulename = modulename
                 
                module = None # Depend on code checking checksum-file to avoid recompilation
    
    - 'S'   - Construct modulename from S, lookup in cache, place in cache after building.
              Three options for this implementation:
              Compiler args must be part of S for import_module(S) to work consistently.
    
                use_cache = True
                moduleids = [signature, signature.signature(), compute_checksum(signature.signature())]
                
                module_path = temp_dir (copied after building)
                modulename = modulename_from_checksum(compute_checksum(S)) # Or just S if valid filename?
                
                module = None # Depend on code checking checksum-file to avoid recompilation
    
    - ''    - Construct S from user file contents and compiler args, see 'S'.

                signature = compute_signature_from_user_files(...)

                use_cache = True
                moduleids = [signature, signature.signature(), compute_checksum(signature.signature())]
                
                module_path = temp_dir (copied after building)
                modulename = modulename_from_checksum(compute_checksum(S)) # Or just S if valid filename?
                
                module = None # Depend on code checking checksum-file to avoid recompilation
    
    I want to do:
        b = jit(a, options)
        b = jit(a, options)
        -> signature = repr(a) + repr(options)
        -> m = import_module(signature)
        -> if not m: m = build_module(..., signature)
        -> return m.myform()
    and:
        b = build_module(modulename, ...)
        b = build_module(modulename, ...)
