Commits on May 17, 2016
  1. Merge branch 'hierarchial_lock_structure'

    * hierarchial_lock_structure:
      Use two-tier hierarchy of locks for fast yet safe access to fd info array
    
    This fixes GitHub issues #27 and #30.
    committed May 17, 2016
Commits on May 13, 2016
  1. Use two-tier hierarchy of locks for fast yet safe access to fd info a…

    …rray
    
    The previous approach relied on a global lock, but acquired it as
    shortly as possible to avoid contention. Since there was no easy way to
    transfer lock ownership between function calls the lock had to be
    released before being acquired again by some of the called functions.
    There was at least one race condition where this was not done in a safe
    manner, and making it safe would have required holding the lock a *very*
    long time, likely leading to a performance regression.
    
    The new approach is a bit more complex because it uses locks per fd
    (i.e. much finer granularity so we don’t hold a global lock while doing
    file system I/O) but does so in a safe manner. The new approach is
    described in an inline code comment, reproduced here:
    
        /* Info about a file descriptor 'fd' is stored in fds[fd]. Before
         * accessing an element, callers MUST both check fds_lock != NULL
         * and then acquire fds_lock[fd] while holding the fds_iter_lock.
         * While any mutex in fds_lock is held, fds_lock will not be freed
         * and set to NULL. */
    
    This should fix GitHub issue #30.
    
    Hindsight is 20/20: Storing the file descriptor n in fds[m] where
    usually n != m is not the most ideal thing to do. So I drive-by fixed
    that: Now info about fd n is always stored in fds[n].
    committed May 13, 2016
  2. Merge pull request #29 from iillyyaa/armv7

    Armv7
    committed May 13, 2016
Commits on May 11, 2016
  1. @iillyyaa
  2. @iillyyaa
Commits on Sep 2, 2015
  1. Makefile: Fix more dependencies

    committed Sep 2, 2015
  2. Fix option parsing

    committed Sep 2, 2015
  3. fix edgecase

    committed Sep 2, 2015
  4. Free page_vec in case of error

    committed Feb 6, 2015
  5. Compute not-cached intervals on file open

    This is probably a huge memory saver, and pushes the computation
    complexity in the open() part, not the close() part.
    
    Instead of storing the whole info vector from mincore, we store it
    temporarily, compute which parts of the file are not cached, and then
    free the vector again, retaining only a singly linked list of byte
    ranges we’ll want to fadvise() on when closing the file.
    
    For example, consider an MP3 file which I played at 0:00, skipped to
    10:00, then skipped to 20:00. It has three small cached regions (plus
    the last page is cached), hence we store three “uncached intervals”.
    On close(), these intervals will be marked “don’t need”. Additionally,
    if the file size changes, everything newly added will be marked “don’t
    need”, as happens with the new file (fd = 4).
    
        $ ./nocache -D /dev/stderr cp ~/musik/fernanda.mp3 /tmp/x
        [nocache] DEBUG: Executing: cp /home/feh/musik/fernanda.mp3 /tmp/x
        [nocache] DEBUG: open(pathname=/home/feh/musik/fernanda.mp3, flags=0x0, mode=00) = 3
        [nocache] DEBUG: fd_get_pageinfo(fd=3): st.st_size=80695431, nr_pages=19702
        [nocache] DEBUG: store_pageinfo(fd=3): pages in cache: 183/19702 (0.9%)  [filesize=78804.1K, pagesize=4K]
        [nocache] DEBUG: open(pathname=/tmp/x, flags=0x201, mode=00) = 4
        [nocache] DEBUG: fd_get_pageinfo(fd=4): st.st_size=0, nr_pages=0
        [nocache] DEBUG: store_pageinfo(fd=4): pages in cache: 0/0 (0.0%)  [filesize=0.0K, pagesize=4K]
        [nocache] DEBUG: fadv_dontneed(fd=4, from=0, len=0 [till new end, file has grown])
        [nocache] DEBUG: close(4)
        [nocache] DEBUG: fadv_dontneed(fd=3, from=245760, len=14266368)
        [nocache] DEBUG: fadv_dontneed(fd=3, from=14761984, len=14258176)
        [nocache] DEBUG: fadv_dontneed(fd=3, from=29270016, len=51425280)
        [nocache] DEBUG: close(3)
    
    It might be beneficial to “chunk” the mincore() call, since one call
    needs filesize/PAGESIZE bytes of memory, so we can fit into 16MB of heap
    memory only the map for a file of size 16*1024^2*4096/1024^3 = 64 GB.
    On terabyte-sized file, we would allocate a tremendous amount of memory,
    and by repeatedly calling mincore() on 64GB-chunks of the file and
    reusing the allocated heap space, we could probably improve performance
    and waste less space. This is for a future commit.
    committed Feb 6, 2015
Commits on Feb 6, 2015
  1. Free fd info array in destructor

    The destructor should only be called on program exit so technically the
    OS should take care of this. But for cleanliness we’ll do it anyway!
    committed Feb 6, 2015
  2. No need for this code

    committed Feb 6, 2015
Commits on Feb 5, 2015
  1. Merge branch 'debug-infrastructure'

    * debug-infrastructure:
      Make the debugging easier to handle for end users
      Implement debugging infrastructure
    committed Feb 5, 2015
  2. Make the debugging easier to handle for end users

    This REQUIRES a Bash (or Zsh) now; POSIX does not guarantee the
    availability of the {var}>/file syntax!
    committed Feb 5, 2015
Commits on Feb 4, 2015
  1. Implement debugging infrastructure

    This is meant to be very simple as to avoid doing any real work in the
    initialization handler. Debugging messages were proposed and inserted by
    Moo Kim <Moo.Kim@Teradata.com>; I have taken the liberty to expand
    the #ifdef-based approach that opened a file to this version. Rationale
    from the discussion with Moo:
    
    > Second, about the debugging. It’s really fragile to do file open
    > operations in a library designed for intercepting file open
    > operations, especially if it’s in the __init__ handler. Instead I have
    > the following idea: In the nocache wrapper, we add a log file argument
    > e.g. -D for debugging, which takes a filename. Before starting the
    > binary, we open the file for writing via
    >
    >     exec 3>${FILENAME}
    >
    > And we export the env variable NOCACHE_DEBUG_FD=3. The executed
    > process will inherit this already opened FD and will just use
    > something like
    >
    >     debugfd = atoi(getenv("NOCACHE_DEBUG_FD"));
    >     debugfp = fdopen(debugfd);
    >     ...
    >     fprintf(debugfp, "DEBUG: ....\n");
    >
    > We simultaneously get rid of the problem of permissions (maybe the
    > process raises/drops its privileges?), race conditions (file is
    > already open and need not be closed at any time) and the ugliness of
    > doing something with actual files that could fail somehow. It’s
    > trivial to implement and clean in my eyes. What do you think about it?
    >
    > In Bash/Zsh, we could actually do better by doing:
    >
    >     exec {debugfd}>${FILENAME}
    >     export NOCACHE_DEBUG_FD=$debugfd
    >
    > (Because fd 3 might be in use, the exec above can fail; this one picks
    > a new fd greater than 10 which is not in use yet.)
    committed Feb 4, 2015
Commits on May 15, 2014
  1. README: Talk about a cgroup approach

    committed May 15, 2014
  2. Makefile: Allow easy compiler switch

    Compiling with “clang” is now as easy as running
    
        make CC=clang
    committed May 15, 2014
Commits on Apr 23, 2014
Commits on Apr 22, 2014
  1. @cubicdaiya

    add LF for usage

    cubicdaiya committed Apr 22, 2014
Commits on Feb 8, 2014
  1. Merge pull request #20 from cicku/patch-1

    Preserve timestamp during install
    committed Feb 8, 2014
  2. @cicku
Commits on Jul 24, 2013
  1. @noushi

    cachestats: Don't treat empty files differently

    Signed-off-by: Julius Plenz <julius@plenz.com>
    noushi committed with Jul 23, 2013
Commits on May 30, 2013
  1. README: Correct Debian package link

    (See issue #14.)
    committed May 30, 2013
Commits on May 29, 2013
  1. Merge branch 'makefile-improvement'

    * makefile-improvement:
      Adjust README for Makefile update.
      GNU Make best practice
    
    (Closes issue #14.)
    committed May 30, 2013
  2. Adjust README for Makefile update.

    committed May 29, 2013
  3. @onlyjob

    GNU Make best practice

    onlyjob committed with May 29, 2013
Commits on May 20, 2013
  1. Block all signals during store_pageinfo() and free_unclaimed_pages()

    The friendly folks at #musl again:
    
    21:34 < dalias> the cheap way around this problem is to block signals for the
       entire duration of the unsafe operation
    21:35 < dalias> formally (by the rules of the standard) this is not sufficient
    21:35 < dalias> because formally it's not just calling pthread_mutex_lock again
       on the same mutex  while the first call is interrupted that's undefined
    21:36 < dalias> it's calling ANY unsafe function while ANY unsafe function (the
       same or otherwise) with any argument (e.g. not necessarily the same mutex)
       that gives undefined behavior
    21:38 < dalias> but real-world-implementations don't have this maximum
       theoretical degree of unsafety
    21:38 < dalias> so the approach i described (just ensuring your functions don't
       interrupt themselves or each other) should be enough to make them safe
    committed May 20, 2013
  2. Assert the existence of original functions

    The friendly folks at #musl again:
    
    21:17 < dalias> if you init the pointers both from the ctor and the "lazy
       init", then the assertion-failure/abort would happen during construction
       (either your ctor or somebody else's ctor calling your functions)
    21:17 < dalias> so the program would exit "before it even gets started"
    21:17 < dalias> and this is not so bad
    
    21:18 < dalias> normally i consider it really bad practice for a library to
       abort the program
    21:18 < dalias> but this happens during ctors, and there truely is no way to
       proceed
    21:18 < dalias> if you've already replaced the program's library functions
       (open, etc.) with your copies, and then find that you can't actually supply
       working ones
    committed May 20, 2013
  3. Supply “lazy initializers” in every intercepted function

    The friendly folks at #musl say:
    
    20:52 < dalias> i think it's good to have both that and the ctor
    20:52 < Feh> This should increase robustness if it is in every function.
    20:52 < dalias> because...
    20:52 < dalias> several of these functions are required to be async-signal-safe
    20:53 < dalias> so if the first call happened to be from a signal handler,
       you'd be in trouble if you didn't already have the ctor
    20:54 < dalias> in theory another ctor in another lib or the app could run
       before yours and install and run a signal handler :-p but that's pretty
       unlikely and pathological
    20:54 < dalias> so i think having both the ctor and the lazy-init keeps you
       safe in situations that will arise in the real world
    committed May 20, 2013