/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkTLS_DEFINED
#define SkTLS_DEFINED

#include "SkTypes.h"

/**
 *  Maintains a per-thread cache, using a CreateProc as the key into that cache.
 */
class SkTLS {
public:
    typedef void* (*CreateProc)();
    typedef void  (*DeleteProc)(void*);

    /**
     *  If Get() has previously been called with this CreateProc, then this
     *  returns its cached data, otherwise it returns nullptr. The CreateProc is
     *  never invoked in Find, it is only used as a key for searching the
     *  cache.
     */
    static void* Find(CreateProc);

    /**
     *  Return the cached data that was returned by the CreateProc. This proc
     *  is only called the first time Get is called, and there after it is
     *  cached (per-thread), using the CreateProc as a key to look it up.
     *
     *  When this thread, or Delete is called, the cached data is removed, and
     *  if a DeleteProc was specified, it is passed the pointer to the cached
     *  data.
     */
    static void* Get(CreateProc, DeleteProc);

    /**
     *  Remove (optionally calling the DeleteProc if it was specificed in Get)
     *  the cached data associated with this CreateProc. If no associated cached
     *  data is found, do nothing.
     */
    static void Delete(CreateProc);

private:
    // Our implementation requires only 1 TLS slot, as we manage multiple values
    // ourselves in a list, with the platform specific value as our head.

    /**
     *  Implemented by the platform, to return the value of our (one) slot per-thread
     *
     *  If forceCreateTheSlot is true, then we must have created the "slot" for
     *  our TLS, even though we know that the return value will be nullptr in that
     *  case (i.e. no-slot and first-time-slot both return nullptr). This ensures
     *  that after calling GetSpecific, we know that we can legally call
     *  SetSpecific.
     *
     *  If forceCreateTheSlot is false, then the impl can either create the
     *  slot or not.
     */
    static void* PlatformGetSpecific(bool forceCreateTheSlot);

    /**
     *  Implemented by the platform, to set the value for our (one) slot per-thread
     *
     *  The implementation can rely on GetSpecific(true) having been previously
     *  called before SetSpecific is called.
     */
    static void  PlatformSetSpecific(void*);

public:
    /**
     *  Will delete our internal list. To be called by the platform if/when its
     *  TLS slot is deleted (often at thread shutdown).
     *
     *  Public *only* for the platform's use, not to be called by a client.
     */
    static void Destructor(void* ptr);
};

#endif