changeset 1158:68a04057057e

Merge with v4.6
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sun, 30 Dec 2018 13:38:08 -0500
parents 3c363a43965a (current diff) 3f0b82ff74b4 (diff)
children 3bdf7a8874c0
files CMakeLists.txt post.c
diffstat 13 files changed, 160 insertions(+), 151 deletions(-) [+]
line wrap: on
line diff
--- a/.hgsigs	Sun Dec 30 13:23:15 2018 -0500
+++ b/.hgsigs	Sun Dec 30 13:38:08 2018 -0500
@@ -3,3 +3,4 @@
 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
+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
--- a/.hgtags	Sun Dec 30 13:23:15 2018 -0500
+++ b/.hgtags	Sun Dec 30 13:38:08 2018 -0500
@@ -13,3 +13,4 @@
 1dc65e2b39e75af9fc4f7a9c1e3517cff4666eea v4.5-rc1
 40e8a3bb0684652444e3090343af2a31ad98da63 v4.5-rc2
 6435f2f8f9181c677db9fdfd42e60937b6966772 v4.5
+fab9a54694929a2b6b6768153f7a880f99f5f8ab v4.6
--- a/CMakeLists.txt	Sun Dec 30 13:23:15 2018 -0500
+++ b/CMakeLists.txt	Sun Dec 30 13:38:08 2018 -0500
@@ -117,7 +117,7 @@
 add_dependencies(blahg revisiontag)
 
 target_link_libraries(blahg
-	md
+	crypto
 	m
 	${JEFFPC_LIBRARY}
 )
--- a/README	Sun Dec 30 13:23:15 2018 -0500
+++ b/README	Sun Dec 30 13:38:08 2018 -0500
@@ -46,15 +46,11 @@
 Dependencies
 ============
 
-At present time, blahgd depends on a number of interfaces and features
-provided by illumos-based distros (e.g., OmniOS or OpenIndiana hipster).
-Specifically, blahgd requires:
-
-  - libmd.so (for SHA1)
-
-Other dependencies include:
+To build blahgd you must have:
 
   - flex & bison
+  - libcrypto
+  - libjeffpc
 
 Building and Installing
 =======================
--- a/file_cache.c	Sun Dec 30 13:23:15 2018 -0500
+++ b/file_cache.c	Sun Dec 30 13:38:08 2018 -0500
@@ -27,6 +27,7 @@
 #include <stdbool.h>
 #include <port.h>
 
+#include <jeffpc/atomic.h>
 #include <jeffpc/val.h>
 #include <jeffpc/synch.h>
 #include <jeffpc/thread.h>
@@ -48,17 +49,11 @@
 static struct rb_tree file_cache;
 static struct lock file_lock;
 
+static atomic64_t current_revision;
+
 static int filemon_port;
 
 static struct mem_cache *file_node_cache;
-static struct mem_cache *file_callback_cache;
-
-struct file_callback {
-	struct list_node list;
-
-	void (*cb)(void *);
-	void *arg;
-};
 
 struct file_node {
 	char *name;			/* the filename */
@@ -68,9 +63,9 @@
 
 	/* everything else is protected by the lock */
 	struct str *contents;		/* cache file contents if allowed */
-	struct list callbacks;		/* list of callbacks to invoke */
 	struct stat stat;		/* the stat info of the cached file */
 	bool needs_reload;		/* caching stale data */
+	uint64_t cache_rev;		/* cache version */
 	struct file_obj fobj;		/* FEN port object */
 };
 
@@ -103,7 +98,6 @@
 static void process_file(struct file_node *node, int events)
 {
 	struct file_obj *fobj = &node->fobj;
-	struct file_callback *fcb;
 	struct stat statbuf;
 
 	MXLOCK(&node->lock);
@@ -125,9 +119,6 @@
 
 		print_event(fobj->fo_name, events);
 
-		list_for_each(fcb, &node->callbacks)
-			fcb->cb(fcb->arg);
-
 		/*
 		 * If the file went away, we could rely on reloading to deal
 		 * with it.  Or we can just remove the node and have the
@@ -136,6 +127,14 @@
 		 */
 		if (events & FILE_EXCEPTION)
 			goto free;
+
+		/*
+		 * Because the cached data is invalid (and therefore
+		 * useless), we can free it now and avoid having it linger
+		 * around, ending up core files, etc.
+		 */
+		str_putref(node->contents);
+		node->contents = NULL;
 	}
 
 	/* re-register */
@@ -168,29 +167,6 @@
 	fn_putref(node); /* put the cache's reference */
 }
 
-static int add_cb(struct file_node *node, void (*cb)(void *), void *arg)
-{
-	struct file_callback *fcb;
-
-	if (!cb)
-		return 0;
-
-	list_for_each(fcb, &node->callbacks)
-		if ((fcb->cb == cb) && (fcb->arg == arg))
-			return 0;
-
-	fcb = mem_cache_alloc(file_callback_cache);
-	if (!fcb)
-		return -ENOMEM;
-
-	fcb->cb = cb;
-	fcb->arg = arg;
-
-	list_insert_tail(&node->callbacks, fcb);
-
-	return 0;
-}
-
 static void *filemon(void *arg)
 {
 	port_event_t pe;
@@ -236,10 +212,6 @@
 					   sizeof(struct file_node), 0);
 	ASSERT(!IS_ERR(file_node_cache));
 
-	file_callback_cache = mem_cache_create("file-callback-cache",
-					       sizeof(struct file_callback), 0);
-	ASSERT(!IS_ERR(file_callback_cache));
-
 	/* start the file event monitor */
 	filemon_port = port_create();
 	ASSERT(filemon_port != -1);
@@ -263,11 +235,10 @@
 	node->fobj.fo_name = node->name;
 	node->contents = NULL;
 	node->needs_reload = true;
+	node->cache_rev = atomic_inc(&current_revision);
 
 	MXINIT(&node->lock, &file_node_lc);
 	refcnt_init(&node->refcnt, 1);
-	list_create(&node->callbacks, sizeof(struct file_callback),
-		    offsetof(struct file_callback, list));
 
 	return node;
 
@@ -278,31 +249,9 @@
 
 static void fn_free(struct file_node *node)
 {
-	struct file_callback *fcb;
-
 	if (!node)
 		return;
 
-#if 0
-	/*
-	 * FIXME: This will always fail
-	 *
-	 * When a post is loaded, it registers a number of callbacks with
-	 * the file cache subsystem.  When the post structures get freed in
-	 * post_destroy(), the callbacks linger on the callbacks list.  If
-	 * we are unlucky, a file change could actually invoke the callback
-	 * with a freed pointer.
-	 *
-	 * We need the post destruction to free all its callbacks but there
-	 * is currently no easy way to do that.
-	 */
-	ASSERT(list_is_empty(&node->callbacks));
-#endif
-
-	/* free all the callbacks */
-	while ((fcb = list_remove_head(&node->callbacks)))
-		mem_cache_free(file_callback_cache, fcb);
-
 	str_putref(node->contents);
 	free(node->name);
 	mem_cache_free(file_node_cache, node);
@@ -339,6 +288,7 @@
 	}
 
 	node->needs_reload = false;
+	node->cache_rev = atomic_inc(&current_revision);
 
 	return 0;
 }
@@ -360,7 +310,7 @@
 	return node;
 }
 
-struct str *file_cache_get_cb(const char *name, void (*cb)(void *), void *arg)
+struct str *file_cache_get(const char *name, uint64_t *rev)
 {
 	struct file_node *out, *tmp;
 	struct file_node key;
@@ -387,14 +337,6 @@
 
 	MXLOCK(&out->lock);
 
-	/* register the callback */
-	if ((ret = add_cb(out, cb, arg))) {
-		/* Dang! An error... time to undo everything */
-		MXUNLOCK(&out->lock);
-		fn_putref(out);
-		return ERR_PTR(ret);
-	}
-
 	/* ...and insert it into the cache */
 	MXLOCK(&file_lock);
 	tmp = rb_find(&file_cache, &key, &where);
@@ -442,6 +384,10 @@
 	}
 
 	str = str_getref(out->contents);
+
+	/* inform the caller about which version we're returning */
+	if (rev)
+		*rev = out->cache_rev;
 	MXUNLOCK(&out->lock);
 
 	/* put the reference for the file node */
@@ -450,6 +396,37 @@
 	return str;
 }
 
+bool file_cache_has_newer(const char *name, uint64_t rev)
+{
+	struct file_node *out;
+	struct file_node key;
+	bool ret;
+
+	key.name = (char *) name;
+
+	/* do we have it? */
+	MXLOCK(&file_lock);
+	out = rb_find(&file_cache, &key, NULL);
+	fn_getref(out);
+	MXUNLOCK(&file_lock);
+
+	/*
+	 * We don't have it cached (which is weird/a bug), so let's pretend
+	 * that there is a newer version...just in case.
+	 */
+	if (!out)
+		return true;
+
+	MXLOCK(&out->lock);
+	ret = out->needs_reload || (rev != out->cache_rev);
+	MXUNLOCK(&out->lock);
+
+	/* put the reference for the file node */
+	fn_putref(out);
+
+	return ret;
+}
+
 void uncache_all_files(void)
 {
 	struct file_node *cur;
--- a/file_cache.h	Sun Dec 30 13:23:15 2018 -0500
+++ b/file_cache.h	Sun Dec 30 13:38:08 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ * Copyright (c) 2014-2018 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -24,15 +24,11 @@
 #define __FILE_CACHE_H
 
 #include <stdlib.h>
+#include <stdbool.h>
 
 extern void init_file_cache(void);
 extern void uncache_all_files(void);
-extern struct str *file_cache_get_cb(const char *name, void (*cb)(void *),
-				     void *arg);
-
-static inline struct str *file_cache_get(const char *name)
-{
-	return file_cache_get_cb(name, NULL, NULL);
-}
+extern struct str *file_cache_get(const char *name, uint64_t *rev);
+extern bool file_cache_has_newer(const char *name, uint64_t rev);
 
 #endif
--- a/listing.c	Sun Dec 30 13:23:15 2018 -0500
+++ b/listing.c	Sun Dec 30 13:38:08 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ * Copyright (c) 2011-2018 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -30,8 +30,6 @@
 #include "file_cache.h"
 #include "post.h"
 
-#define PAGE 4096
-
 struct str *listing(struct post *post, const char *fname)
 {
 	char path[FILENAME_MAX];
@@ -40,8 +38,7 @@
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/%s",
 		 str_cstr(config.data_dir), post->id, fname);
 
-	in = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-			       post);
+	in = post_get_cached_file(post, path);
 	if (IS_ERR(in))
 		goto err;
 
--- a/math.c	Sun Dec 30 13:23:15 2018 -0500
+++ b/math.c	Sun Dec 30 13:38:08 2018 -0500
@@ -20,7 +20,7 @@
  * SOFTWARE.
  */
 
-#include <sha1.h>
+#include <openssl/sha.h>
 
 #include <jeffpc/val.h>
 #include <jeffpc/synch.h>
@@ -157,16 +157,16 @@
 	char pngpath[FILENAME_MAX];
 
 	const char *tex = str_cstr(val);
-	SHA1_CTX digest;
+	SHA_CTX digest;
 	struct stat statbuf;
 	unsigned char md[20];
 	char amd[41];
 	uint32_t id;
 	int ret;
 
-	SHA1Init(&digest);
-	SHA1Update(&digest, tex, strlen(tex));
-	SHA1Final(md, &digest);
+	VERIFY3S(SHA1_Init(&digest), ==, 1);
+	VERIFY3S(SHA1_Update(&digest, tex, strlen(tex)), ==, 1);
+	VERIFY3S(SHA1_Final(md, &digest), ==, 1);
 
 	hexdumpz(amd, md, 20, true);
 
--- a/post.c	Sun Dec 30 13:23:15 2018 -0500
+++ b/post.c	Sun Dec 30 13:38:08 2018 -0500
@@ -83,15 +83,36 @@
 	init_post_index();
 }
 
-void revalidate_post(void *arg)
+struct str *post_get_cached_file(struct post *post, const char *path)
 {
-	struct post *post = arg;
+	struct str *out;
+	uint64_t rev;
+	int err;
+
+	out = file_cache_get(path, &rev);
+	if (IS_ERR(out))
+		return out;
 
-	DBG("%s: marking post #%u for refresh", __func__, post->id);
+	err = nvl_set_int(post->files, path, rev);
+	if (err) {
+		str_putref(out);
+		out = ERR_PTR(err);
+	}
+
+	return out;
+}
 
-	post_lock(post, false);
-	post->needs_refresh = true;
-	post_unlock(post);
+static void post_remove_all_filenames(struct post *post)
+{
+	const struct nvpair *pair;
+
+	while ((pair = nvl_iter_start(post->files)) != NULL) {
+		struct str *name = nvpair_name_str(pair);
+
+		VERIFY0(nvl_unset(post->files, str_cstr(name)));
+
+		str_putref(name);
+	}
 }
 
 /* consumes the struct val reference */
@@ -140,20 +161,14 @@
 static struct str *load_comment(struct post *post, int commid)
 {
 	char path[FILENAME_MAX];
-	struct str *err_msg;
 	struct str *out;
 
-	err_msg = STATIC_STR("Error: could not load comment text.");
-
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/comments/%d/text.txt",
 		 str_cstr(config.data_dir), post->id, commid);
 
-	out = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-				post);
+	out = post_get_cached_file(post, path);
 	if (IS_ERR(out))
-		out = err_msg;
-	else
-		str_putref(err_msg);
+		out = STATIC_STR("Error: could not load comment text.");
 
 	return out;
 }
@@ -169,8 +184,7 @@
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/comments/%d/meta.lisp",
 		 str_cstr(config.data_dir), post->id, commid);
 
-	meta = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-				 post);
+	meta = post_get_cached_file(post, path);
 	ASSERT(!IS_ERR(meta));
 
 	lv = sexpr_parse_str(meta);
@@ -336,8 +350,7 @@
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/post.%s",
 		 str_cstr(config.data_dir), post->id, exts[post->fmt]);
 
-	raw = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-				post);
+	raw = post_get_cached_file(post, path);
 	if (IS_ERR(raw))
 		return PTR_ERR(raw);
 
@@ -383,8 +396,7 @@
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/post.lisp",
 		 str_cstr(config.data_dir), post->id);
 
-	meta = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-				 post);
+	meta = post_get_cached_file(post, path);
 	if (IS_ERR(meta))
 		return PTR_ERR(meta);
 
@@ -412,10 +424,48 @@
 	return 0;
 }
 
-static int __refresh(struct post *post)
+static bool must_refresh(struct post *post)
+{
+	const struct nvpair *pair;
+
+	if (post->preview)
+		return true; /* always refresh previews */
+
+	if (nvl_iter_start(post->files) == NULL)
+		return true; /* no files means we have no idea what is needed */
+
+	nvl_for_each(pair, post->files) {
+		struct str *name = nvpair_name_str(pair);
+		uint64_t file_rev;
+
+		ASSERT0(nvpair_value_int(pair, &file_rev));
+
+		if (!file_cache_has_newer(str_cstr(name), file_rev)) {
+			str_putref(name);
+			continue;
+		}
+
+		cmn_err(CE_DEBUG, "post %u needs a refresh "
+			"('%s' changed, old rev %"PRIu64")", post->id,
+			str_cstr(name), file_rev);
+
+		str_putref(name);
+
+		return true; /* no need to check oher files, we are refreshing */
+	}
+
+	return false;
+}
+
+int post_refresh(struct post *post)
 {
 	int ret;
 
+	if (!must_refresh(post))
+		return 0;
+
+	post_remove_all_filenames(post);
+
 	str_putref(post->title);
 	post->title = NULL;
 
@@ -432,17 +482,9 @@
 	if ((ret = __load_post_body(post)))
 		return ret;
 
-	post->needs_refresh = false;
-
 	return 0;
 }
 
-void post_refresh(struct post *post)
-{
-	ASSERT0(post->preview);
-	ASSERT0(__refresh(post));
-}
-
 struct post *load_post(int postid, bool preview)
 {
 	struct post *post;
@@ -470,7 +512,6 @@
 	post->body = NULL;
 	post->numcom = 0;
 	post->preview = preview;
-	post->needs_refresh = true;
 
 	rb_create(&post->tags, tag_cmp, sizeof(struct post_tag),
 		  offsetof(struct post_tag, node));
@@ -481,7 +522,14 @@
 	refcnt_init(&post->refcnt, 1);
 	MXINIT(&post->lock, &post_lc);
 
-	if ((err = __refresh(post)))
+	post->files = nvl_alloc();
+	if (IS_ERR(post->files)) {
+		err = PTR_ERR(post->files);
+		post->files = NULL;
+		goto err_free;
+	}
+
+	if ((err = post_refresh(post)))
 		goto err_free;
 
 	if (!post->preview)
@@ -491,6 +539,7 @@
 
 err_free:
 	post_destroy(post);
+
 err:
 	cmn_err(CE_ERROR, "Failed to load post id %u: %s", postid,
 		xstrerror(err));
@@ -518,6 +567,8 @@
 	post_remove_all_tags(&post->cats);
 	post_remove_all_comments(post);
 
+	nvl_putref(post->files);
+
 	str_putref(post->title);
 	str_putref(post->body);
 
--- a/post.h	Sun Dec 30 13:23:15 2018 -0500
+++ b/post.h	Sun Dec 30 13:38:08 2018 -0500
@@ -55,7 +55,6 @@
 
 	struct lock lock;
 
-	bool needs_refresh;
 	bool preview;
 	bool listed;
 
@@ -77,16 +76,18 @@
 	struct str *body;
 
 	struct str *twitter_img;
+
+	/* filenames used to construct this post */
+	struct nvlist *files;
 };
 
 struct req;
 
 extern void init_post_subsys(void);
+extern struct str *post_get_cached_file(struct post *post, const char *path);
 extern struct post *load_post(int postid, bool preview);
-extern void post_refresh(struct post *post);
+extern int post_refresh(struct post *post);
 extern void post_destroy(struct post *post);
-extern void revalidate_post(void *arg);
-extern void revalidate_all_posts(void *arg);
 extern void load_posts(struct req *req, struct post **posts, int nposts,
 		       bool moreposts);
 extern int load_all_posts(void);
@@ -107,12 +108,9 @@
 
 REFCNT_INLINE_FXNS(struct post, post, refcnt, post_destroy, NULL)
 
-static inline void post_lock(struct post *post, bool allow_refresh)
+static inline void post_lock(struct post *post)
 {
 	MXLOCK(&post->lock);
-
-	if (allow_refresh && post->needs_refresh)
-		post_refresh(post);
 }
 
 static inline void post_unlock(struct post *post)
--- a/post_index.c	Sun Dec 30 13:23:15 2018 -0500
+++ b/post_index.c	Sun Dec 30 13:38:08 2018 -0500
@@ -486,16 +486,6 @@
 	return ret;
 }
 
-void revalidate_all_posts(void *arg)
-{
-	struct post_global_index_entry *cur;
-
-	MXLOCK(&index_lock);
-	rb_for_each(&index_global, cur)
-		revalidate_post(cur->post);
-	MXUNLOCK(&index_lock);
-}
-
 void index_for_each_tag(int (*init)(void *, unsigned long),
 			void (*step)(void *, struct str *, unsigned long,
 				     unsigned long, unsigned long),
--- a/post_nv.c	Sun Dec 30 13:23:15 2018 -0500
+++ b/post_nv.c	Sun Dec 30 13:38:08 2018 -0500
@@ -169,7 +169,8 @@
 	if (!post)
 		return NULL;
 
-	post_lock(post, true);
+	post_lock(post);
+	ASSERT0(post_refresh(post));
 
 	out = __store_vars(req, post, titlevar);
 
@@ -201,7 +202,8 @@
 	for (i = 0; i < nposts; i++) {
 		struct post *post = posts[i];
 
-		post_lock(post, true);
+		post_lock(post);
+		ASSERT0(post_refresh(post));
 
 		nvposts[nnvposts] = nvl_cast_to_val(__store_vars(req, post, NULL));
 		if (IS_ERR(nvposts[nnvposts])) {
--- a/render.c	Sun Dec 30 13:23:15 2018 -0500
+++ b/render.c	Sun Dec 30 13:38:08 2018 -0500
@@ -67,7 +67,7 @@
 	snprintf(path, sizeof(path), "templates/%s/%s.tmpl", str_cstr(req->fmt),
 		 tmpl);
 
-	raw = file_cache_get_cb(path, revalidate_all_posts, NULL);
+	raw = file_cache_get(path, NULL);
 	if (IS_ERR(raw))
 		return NULL;