Categories
Coding

TimedMap Implementation

Here is the implementation for a timed concurrent map implementation that I wrote a while ago. It delegates to an underlying ConcurrentMap, and uses a single ReentrantLock to synchronize writers. It is designed to use in situations where objects eventually age themselves out of cache storage.

package com.researchkitchen.map;

import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/**
 * A wrapper around a {@link ConcurrentMap} that expires entries from the underlying map
 * after a specific period (the TTL value) has expired. 
 * @author winstor
 *
 * @param <K>
 * @param <V>
 */
public final class TimedMap<K,V> {
    private final ConcurrentMap<K, ExpiredObject<K,V>> map = new ConcurrentHashMap<K, ExpiredObject<K,V>>();
    private final Lock writeLock = new ReentrantLock();
    private static final long DEFAULT_TTL = 60000L;
    private final Timer timer = new Timer("TimedMap Timer", true);
    /**
     * A wrapper for a underlying object that associates a {@link TimerTask} instance
     * with the object.
     * @author winstor
     *
     * @param <K> The key type K
     * @param <V> The value type V
     */
    class ExpiredObject<K,V> {
        private final V value;
        private final ExpiryTask<K> task;
        private final long ttl;

        public ExpiredObject(K key, V value) {
            this(key, value, DEFAULT_TTL);
        }

        public ExpiredObject(K key, V value, long ttl) {
            this.value = value;
            this.task = new ExpiryTask<K>(key);
            this.ttl = ttl;
            timer.schedule(this.task, ttl);
        }

        public ExpiryTask<K> getTask()   { return task; }
        public V getValue()              { return value; }
        public long getTtl()             { return ttl; }
    }

    /**
     * A {@link TimerTask} implementation that removes its
     * associated entry (identified by a key K) from the
     * internal {@link Map}.
     * @author winstor
     *
     * @param <K> The object key
     */
    class ExpiryTask<K> extends TimerTask {
        private final K key;

        public ExpiryTask(K key) {
            this.key = key;
        }

        public K getKey() {
            return key;
        }

        @Override
        public void run() {
            System.out.println("Expiring element with key [" + key + "]");
            try {
                writeLock.lock();
                if (map.containsKey(key))
                    map.remove(getKey());
            }
            finally {
                writeLock.unlock();
            }
        }
    }

    /**
     * Insert an entry into the underlying map, specifying a time-to-live
     * for the element to be inserted.
     * <p/>
     * @param key The key of the element to be inserted.
     * @param value The item to be inserted.
     * @param expiry A time-to-live value (specified in milliseconds).
     */
    @SuppressWarnings("unchecked")
    public void put(K key, V value, long expiry) {
        try {
            writeLock.lock();

            final ExpiredObject<K, V> object =
                map.putIfAbsent(key, new ExpiredObject<K, V>(key, value, expiry));
            /* Was there an existing entry for this key? If so, cancel the existing timer */
            if (object != null)
                object.getTask().cancel();
        }
        finally {
            writeLock.unlock();
        }
    }

    /**
     * Insert an entry into the map with a default time-to-live value.
     * @param key The key of the element to be inserted.
     * @param value The item to be inserted.
     */
    public void put(K key, V value) {
        put(key, value, DEFAULT_TTL);
    }

    /**
     * Retrieve the entry identified by <code>key</code>, or <code>null</code> if
     * it does not exist.
     * @param key The key identifying the entry to retrieve
     * @return The entry corresponding to <code>key</code>, or <code>null</code>.
     */
    @SuppressWarnings("unchecked")
    public V get(K key) {
        return (V) (map.containsKey(key) ? map.get(key).getValue() : null);
    }

    /**
     * Clear the underlying map and cancel any outstanding expiry timers.
     */
    public void clear() {
        try {
            writeLock.lock();
            for (ExpiredObject<K, V> obj : map.values()) {
                obj.getTask().cancel();
            }
            map.clear();
            timer.purge();  // Force removal of all cancelled tasks
        }
        finally {
            writeLock.unlock();                                                                                                                                                                                                                                                                                                                                                
        }
    }

    public boolean containsKey(K key) {
        return map.containsKey(key);
    }

    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
     * Remov the element identified by <code>key</code> from the map, returning <code>null</code>
     * if it does not exist.
     * @param key The key identifying the element to remove
     * @return The removed element, or null if it was not present.
     */
    public V remove(K key) {
        final ExpiredObject<K,V> object;
        try {
            writeLock.lock();
            System.out.println("Removing element with key:" + key);
            object = map.remove(key);
            if (object != null)
                object.getTask().cancel();
        }
        finally {
            writeLock.unlock();
        }
        return (object == null ? null : object.getValue());
    }

    public int size() {
        return map.size();
    }
}

Categories
Finance R

Presentation at UseR! 2008

Today is the second day of UseR! 2008 in Germany, and I will be giving a short talk on a market data interface I developed for R a while back. The confererence is apparently the largest R user conference yet, with over 400 participants, from all areas of industry and academia.

Here are the slides:

r_market_data

For some reason, I couldn’t get the Beamer package to number my contents correctly, so it looks a little strange.

Categories
Coding R

The Problem With #defines

The C/C++ #define mechanism is a very powerful, but very blunt templating approach. It’s a simple textual search-and-replace mechanism, which can be very useful, but not so subtle.

This was illustrated to me today when I ran into a problem with some code for an R extension that I am writing, that interfaces to Reuters market data feeds. The offending code is shown below:


RTRXFileDb* configDb = new RTRXFileDb(configPath);
if (configDb->error()) {
      Rf_error("Error initializing SFC: %s", configDb->errorText());
            return ScalarReal(-1);
}

This code creates an object instance, and checks the boolean member property error() property for problems. If there is a problem, it calls the R function error() to print the error to the R console.

When I tried to compile this code, however, I got the following error:

error C2039: ‘Rf_error’ : is not a member of ‘RTRXFileDb’

This initially confused me, but then I looked at the R header file error.h and found the definition of error():

#define R_ERROR_H_

#ifdef  __cplusplus
extern "C" {
#endif

void    Rf_error(const char *, ...);
void    Rf_warning(const char *, ...);
void    WrongArgCount(const char *);
void    UNIMPLEMENTED(const char *);
void    R_ShowMessage(const char *s);
    

#ifdef  __cplusplus
}
#endif

#ifndef R_NO_REMAP
#define error Rf_error
#define warning Rf_warning
#endif

#endif /* R_ERROR_H_ */

So error is mapped to Rf_error via a #define directive, meaning that whenever the preprocessor encountered the “error” token, it went ahead and replaced it with the R-mapped definition, regardless of its syntactic context. This is normally not a problem, apart from when we get name collisions, as we have here. There are a few potential ways to fix this, none of them ideal. Some of the options are:

  • #define R_NO_REMAP in the preprocessor, which will stop error being mapped to Rf_error, and just use Rf_error explicitly. The problem with this approach is that R_NO_REMAP will undefine a whole bunch of other R functions, making it tedious to replace every one.
  • Change the R header file: either change the #define block to map Rf_error to _error, or some alternative name, and just change the usages of that, or alternatively remove the error mapping completely.
  • Use #undef error and just use Rf_error explicitly (Pointed out on Reddit)

I’m going to go for one of the latter options, as they are the path of least resistance. This was actually a very small issue. But name collisions of this type can potentially be nasty, or at the very least tedious to fix.