summaryrefslogtreecommitdiffstats
path: root/cpp/sca/modules/cache
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cpp/sca/modules/cache/Makefile.am13
-rw-r--r--cpp/sca/modules/cache/cache.hpp133
-rw-r--r--cpp/sca/modules/cache/diskcache-test.cpp (renamed from cpp/sca/modules/cache/cache-test.cpp)40
-rwxr-xr-xcpp/sca/modules/cache/diskcached-test27
-rw-r--r--cpp/sca/modules/cache/memcache-test.cpp96
-rwxr-xr-xcpp/sca/modules/cache/memcached-test2
-rw-r--r--cpp/sca/modules/cache/memcached.hpp143
7 files changed, 372 insertions, 82 deletions
diff --git a/cpp/sca/modules/cache/Makefile.am b/cpp/sca/modules/cache/Makefile.am
index 581b8b6682..0597e91804 100644
--- a/cpp/sca/modules/cache/Makefile.am
+++ b/cpp/sca/modules/cache/Makefile.am
@@ -15,12 +15,17 @@
# specific language governing permissions and limitations
# under the License.
-noinst_PROGRAMS = cache-test
+noinst_PROGRAMS = diskcache-test memcache-test
+
+nobase_include_HEADERS = *.hpp
INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE}
-cache_test_SOURCES = cache-test.cpp
-cache_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
+diskcache_test_SOURCES = diskcache-test.cpp
+diskcache_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
+
+memcache_test_SOURCES = memcache-test.cpp
+memcache_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
-TESTS = memcached-test
+TESTS = memcached-test diskcached-test
diff --git a/cpp/sca/modules/cache/cache.hpp b/cpp/sca/modules/cache/cache.hpp
index d4a24ca3d9..ba66021f3d 100644
--- a/cpp/sca/modules/cache/cache.hpp
+++ b/cpp/sca/modules/cache/cache.hpp
@@ -23,100 +23,97 @@
#define tuscany_cache_hpp
/**
- * Memcached access functions.
+ * Simple cache monad implementation.
*/
-#include "apr.h"
-#include "apu.h"
-#include "apr_general.h"
-#include "apr_strings.h"
-#include "apr_hash.h"
-#include "apr_memcache.h"
-#include "apr_network_io.h"
+#include <sys/stat.h>
#include <string>
-#include "list.hpp"
#include "value.hpp"
#include "monad.hpp"
namespace tuscany {
namespace cache {
-class Cache {
+/**
+ * Cached monad. Used to represent a value that can be cached.
+ * To get the value in the monad, just cast it to the value type.
+ */
+template<typename V> class cached {
public:
- Cache() {
- apr_pool_create(&pool, NULL);
- apr_memcache_create(pool, 1, 0, &mc);
+ cached() : mtime(0) {
}
- ~Cache() {
- apr_pool_destroy(pool);
+
+ cached(const lambda<V()>& lvalue, const lambda<unsigned long()> ltime) : lvalue(lvalue), ltime(ltime), mtime(0) {
}
- operator apr_memcache_t*() const {
- return mc;
+ cached(const lambda<V()>& lvalue, const lambda<unsigned long()> ltime, const unsigned long mtime, const V& v) : lvalue(lvalue), ltime(ltime), mtime(mtime), v(v) {
}
- operator apr_pool_t*() const {
- return pool;
+ cached(const cached<V>& c) : lvalue(c.lvalue), ltime(c.ltime), mtime(c.mtime), v(c.v) {
}
-private:
- apr_pool_t* pool;
- apr_memcache_t* mc;
+ operator const V() const {
+ return v;
+ }
-};
+ const cached<V>& operator=(const cached<V>& c) {
+ if(this == &c)
+ return *this;
+ this->lvalue = c.lvalue;
+ this->ltime = c.ltime;
+ this->mtime = c.mtime;
+ this->v = c.v;
+ return *this;
+ }
-const failable<bool, std::string> addServer(const std::string& host, const int port, Cache& cache) {
- apr_memcache_server_t *server;
- const apr_status_t sc = apr_memcache_server_create(cache, host.c_str(), port, 0, 1, 1, 60, &server);
- if (sc != APR_SUCCESS)
- return "Could not create server";
- const apr_status_t as = apr_memcache_add_server(cache, server);
- if (as != APR_SUCCESS)
- return "Could not add server";
- return true;
-}
+ const bool operator!=(const cached<V>& m) const {
+ return !this->operator==(m);
+ }
-const failable<bool, std::string> post(const value& key, const value& val, Cache& cache) {
- const std::string v(val);
- const apr_status_t rc = apr_memcache_add(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27);
- if (rc != APR_SUCCESS)
- return "Could not add entry";
- return true;
-}
+ const bool operator==(const cached<V>& m) const {
+ if (this == &m)
+ return true;
+ return mtime == m.mtime && v == m.v;
+ }
-const failable<bool, std::string> put(const value& key, const value& val, Cache& cache) {
- const std::string v(val);
- const apr_status_t rc = apr_memcache_replace(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27);
- if (rc != APR_SUCCESS)
- return "Could not add entry";
- return true;
-}
+private:
+ lambda<V()> lvalue;
+ lambda<time_t()> ltime;
+ unsigned long mtime;
+ V v;
-const failable<value, std::string> get(const value& key, Cache& cache) {
- apr_pool_t* vpool;
- const apr_status_t pc = apr_pool_create(&vpool, cache);
- if (pc != APR_SUCCESS)
- return std::string("Could not allocate memory");
-
- char *data;
- apr_size_t size;
- const apr_status_t rc = apr_memcache_getp(cache, cache, std::string(key).c_str(), &data, &size, NULL);
- if (rc != APR_SUCCESS) {
- apr_pool_destroy(vpool);
- return std::string("Could not get entry");
- }
+ template<typename X> friend const cached<X> latest(const cached<X>& c);
+ template<typename X> friend std::ostream& operator<<(std::ostream& out, const cached<X>& c);
+};
- const value val(std::string(data, size));
- apr_pool_destroy(vpool);
- return val;
+/**
+ * Write a cached monad to a stream.
+ */
+template<typename V> std::ostream& operator<<(std::ostream& out, const cached<V>& c) {
+ out << c.v;
+ return out;
}
-const failable<bool, std::string> del(const value& key, Cache& cache) {
- const apr_status_t rc = apr_memcache_delete(cache, std::string(key).c_str(), 0);
- if (rc != APR_SUCCESS)
- return "Could not add entry";
- return true;
+/**
+ * Returns the latest value of a cached monad.
+ */
+template<typename V> const cached<V> latest(const cached<V>& c) {
+ unsigned long nt = c.ltime();
+ if (nt == c.mtime)
+ return c;
+ return cached<V>(c.lvalue, c.ltime, nt, c.lvalue());
+}
+
+/**
+ * Returns the latest modification time of a file.
+ */
+const unsigned long latestFileTime(const std::string& path) {
+ struct stat st;
+ int rc = stat(path.c_str(), &st);
+ if (rc < 0)
+ return 0;
+ return st.st_mtim.tv_nsec;
}
}
diff --git a/cpp/sca/modules/cache/cache-test.cpp b/cpp/sca/modules/cache/diskcache-test.cpp
index 776ac72363..3b21de7b9e 100644
--- a/cpp/sca/modules/cache/cache-test.cpp
+++ b/cpp/sca/modules/cache/diskcache-test.cpp
@@ -20,28 +20,50 @@
/* $Rev$ $Date$ */
/**
- * Test Memcached access functions.
+ * Test cache functions.
*/
#include <assert.h>
+#include <sys/time.h>
+#include <time.h>
#include <iostream>
#include <string>
+#include <fstream>
+#include "slist.hpp"
#include "cache.hpp"
namespace tuscany {
namespace cache {
+const std::string fileRead(const std::string path) {
+ std::ifstream is(path);
+ return car(streamList(is));
+}
+
bool testCache() {
- Cache cache;
- addServer("localhost", 11311, cache);
+ const std::string p("tmp/test.txt");
+ const lambda<std::string(std::string)> fr(fileRead);
+ const lambda<time_t(std::string)> ft(latestFileTime);
+
+ const cached<std::string> c(curry(fr, p), curry(ft, p));
+
+ {
+ std::ofstream os(p);
+ os << "initial";
+ os.close();
+ assert(std::string(latest(c)) == std::string("initial"));
+ }
- assert(hasValue(post("a", "AAA", cache)));
- assert(get("a", cache) == value(std::string("AAA")));
- assert(hasValue(put("a", "aaa", cache)));
- assert(get("a", cache) == value(std::string("aaa")));
- assert(hasValue(del("a", cache)));
- assert(!hasValue(get("a", cache)));
+ usleep(1000000);
+ {
+ std::ofstream os(p);
+ os << "updated";
+ os.close();
+ assert(latest(c) != c);
+ assert(std::string(latest(c)) == std::string("updated"));
+ assert(latest(c) == latest(c));
+ }
return true;
}
diff --git a/cpp/sca/modules/cache/diskcached-test b/cpp/sca/modules/cache/diskcached-test
new file mode 100755
index 0000000000..4c10abeceb
--- /dev/null
+++ b/cpp/sca/modules/cache/diskcached-test
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+mkdir -p tmp
+
+# Test
+./diskcache-test
+rc=$?
+
+return $rc
diff --git a/cpp/sca/modules/cache/memcache-test.cpp b/cpp/sca/modules/cache/memcache-test.cpp
new file mode 100644
index 0000000000..3522094126
--- /dev/null
+++ b/cpp/sca/modules/cache/memcache-test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test Memcached access functions.
+ */
+
+#include <assert.h>
+#include <sys/time.h>
+#include <time.h>
+#include <iostream>
+#include <string>
+#include <fstream>
+#include "memcached.hpp"
+
+namespace tuscany {
+namespace cache {
+
+bool testMemCached() {
+ memcached::Cache cache;
+ memcached::addServer("localhost", 11311, cache);
+
+ assert(hasValue(memcached::post("a", "AAA", cache)));
+ assert(memcached::get("a", cache) == value(std::string("AAA")));
+ assert(hasValue(memcached::put("a", "aaa", cache)));
+ assert(memcached::get("a", cache) == value(std::string("aaa")));
+ assert(hasValue(memcached::del("a", cache)));
+ assert(!hasValue(memcached::get("a", cache)));
+
+ return true;
+}
+
+const double duration(struct timeval start, struct timeval end, int count) {
+ long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
+ return (double)t / (double)count;
+}
+
+bool testGetLoop(const int count, memcached::Cache& cache) {
+ if (count == 0)
+ return true;
+ assert(memcached::get("c", cache) == value(std::string("CCC")));
+ return testGetLoop(count - 1, cache);
+}
+
+bool testGetPerf() {
+ const int count = 50;
+ struct timeval start;
+ struct timeval end;
+ {
+ memcached::Cache cache;
+ memcached::addServer("localhost", 11311, cache);
+ assert(hasValue(memcached::post("c", "CCC", cache)));
+
+ testGetLoop(5, cache);
+
+ gettimeofday(&start, NULL);
+
+ testGetLoop(count, cache);
+
+ gettimeofday(&end, NULL);
+ std::cout << "Memcached get test " << duration(start, end, count) << " ms" << std::endl;
+ }
+ return true;
+}
+
+}
+}
+
+int main() {
+ std::cout << "Testing..." << std::endl;
+
+ tuscany::cache::testMemCached();
+ tuscany::cache::testGetPerf();
+
+ std::cout << "OK" << std::endl;
+
+ return 0;
+}
diff --git a/cpp/sca/modules/cache/memcached-test b/cpp/sca/modules/cache/memcached-test
index 2f9870a49b..d0e0dff3de 100755
--- a/cpp/sca/modules/cache/memcached-test
+++ b/cpp/sca/modules/cache/memcached-test
@@ -23,7 +23,7 @@ $cmd &
sleep 1
# Test
-./cache-test
+./memcache-test
rc=$?
# Cleanup
diff --git a/cpp/sca/modules/cache/memcached.hpp b/cpp/sca/modules/cache/memcached.hpp
new file mode 100644
index 0000000000..28bdc0fc89
--- /dev/null
+++ b/cpp/sca/modules/cache/memcached.hpp
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_memcached_hpp
+#define tuscany_memcached_hpp
+
+/**
+ * Memcached access functions.
+ */
+
+#include "apr.h"
+#include "apu.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_hash.h"
+#include "apr_memcache.h"
+#include "apr_network_io.h"
+
+#include <string>
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace memcached {
+
+/**
+ * Represents a memcached context.
+ */
+class Cache {
+public:
+ Cache() {
+ apr_pool_create(&pool, NULL);
+ apr_memcache_create(pool, 1, 0, &mc);
+ }
+ ~Cache() {
+ apr_pool_destroy(pool);
+ }
+
+ operator apr_memcache_t*() const {
+ return mc;
+ }
+
+ operator apr_pool_t*() const {
+ return pool;
+ }
+
+private:
+ apr_pool_t* pool;
+ apr_memcache_t* mc;
+
+};
+
+/**
+ * Add a server to the memcached context.
+ */
+const failable<bool, std::string> addServer(const std::string& host, const int port, const Cache& cache) {
+ apr_memcache_server_t *server;
+ const apr_status_t sc = apr_memcache_server_create(cache, host.c_str(), port, 0, 1, 1, 60, &server);
+ if (sc != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not create server");
+ const apr_status_t as = apr_memcache_add_server(cache, server);
+ if (as != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not add server");
+ return true;
+}
+
+/**
+ * Post a new item to the cache.
+ */
+const failable<bool, std::string> post(const value& key, const value& val, const Cache& cache) {
+ const std::string v(val);
+ const apr_status_t rc = apr_memcache_add(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27);
+ if (rc != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not add entry");
+ return true;
+}
+
+/**
+ * Update an item in the cache.
+ */
+const failable<bool, std::string> put(const value& key, const value& val, const Cache& cache) {
+ const std::string v(val);
+ const apr_status_t rc = apr_memcache_replace(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27);
+ if (rc != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not add entry");
+ return true;
+}
+
+/**
+ * Get an item from the cache.
+ */
+const failable<value, std::string> get(const value& key, const Cache& cache) {
+ apr_pool_t* vpool;
+ const apr_status_t pc = apr_pool_create(&vpool, cache);
+ if (pc != APR_SUCCESS)
+ return mkfailure<value, std::string>("Could not allocate memory");
+
+ char *data;
+ apr_size_t size;
+ const apr_status_t rc = apr_memcache_getp(cache, cache, std::string(key).c_str(), &data, &size, NULL);
+ if (rc != APR_SUCCESS) {
+ apr_pool_destroy(vpool);
+ return mkfailure<value, std::string>("Could not get entry");
+ }
+
+ const value val(std::string(data, size));
+ apr_pool_destroy(vpool);
+ return val;
+}
+
+/**
+ * Delete an item from the cache
+ */
+const failable<bool, std::string> del(const value& key, const Cache& cache) {
+ const apr_status_t rc = apr_memcache_delete(cache, std::string(key).c_str(), 0);
+ if (rc != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not add entry");
+ return true;
+}
+
+}
+}
+
+#endif /* tuscany_memcached_hpp */