diff --git a/Filelist b/Filelist
index b324933..a50f2b4 100644
--- a/Filelist
+++ b/Filelist
@@ -43,6 +43,8 @@ SRC_ALL =	\
 		src/memline.c \
 		src/menu.c \
 		src/message.c \
+		src/message_queue.c \
+		src/message_queue.h \
 		src/misc1.c \
 		src/misc2.c \
 		src/move.c \
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index be0e667..c609584 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -6455,6 +6455,9 @@ lua			Compiled with Lua interface |Lua|.
 mac			Macintosh version of Vim.
 macunix			Macintosh version of Vim, using Unix files (OS-X).
 menu			Compiled with support for |:menu|.
+messagequeue		Compiled with a message queue that scripting languages
+			languages (python, ruby...) can use to safely call
+			vim from other threads.
 mksession		Compiled with support for |:mksession|.
 modify_fname		Compiled with file name modifiers. |filename-modifiers|
 mouse			Compiled with support mouse.
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 4a1c64a..e78279f 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -357,6 +357,7 @@ N  *+localmap*		Support for mappings local to a buffer |:map-local|
 m  *+lua*		|Lua| interface
 m  *+lua/dyn*		|Lua| interface |/dyn|
 N  *+menu*		|:menu|
+   *+messagequeue*	Compiled with thread-safe message queue.
 N  *+mksession*		|:mksession|
 N  *+modify_fname*	|filename-modifiers|
 N  *+mouse*		Mouse handling |mouse-using|
diff --git a/snake.vim b/snake.vim
new file mode 100644
index 0000000..fec34bd
--- /dev/null
+++ b/snake.vim
@@ -0,0 +1,127 @@
+set nocompatible
+set nocursorline
+
+python << EOF
+# adapted from https://gist.github.com/sanchitgangwar/2158084
+import vim
+from random import randint
+from time import sleep
+from threading import Thread, Lock
+ 
+buf = vim.current.buffer
+empty = ' ' * 80
+buf[0] = empty
+for n in xrange(20):
+    buf.append(empty)
+
+
+def addstr(lnum, cnum, string):
+    line = buf[lnum]
+    line = line[0:cnum] + string + line[cnum + len(string):]
+    buf[lnum] = line
+
+
+key = 'right'
+prevKey = 'right'
+score = 0
+snake = [[4,10], [4,9], [4,8]]                                     # Initial snake co-ordinates
+food = [10,20]                                                     # First food co-ordinates
+lock = Lock()
+     
+def run_game():
+    global key, prevKey, score, snake, food
+
+    timeout = None
+     
+    addstr(food[0], food[1], '*')                                      # Prints the food
+     
+    while key != 'esc':
+	lock.acquire()
+	if key == 'space':                                             # If SPACE BAR is pressed, wait for another
+	    key = None
+	    while key != 'space':
+		lock.release()
+		sleep(timeout)
+		lock.acquire()
+	    key = prevKey
+	    lock.release()
+	    continue
+     
+	# Calculates the new coordinates of the head of the snake. NOTE: len(snake) increases.
+	# This is taken care of later at [1].
+	snake.insert(0, [snake[0][0] + (key == 'down' and 1) + (key == 'up' and -1), snake[0][1] + (key == 'left' and -1) + (key == 'right' and 1)])
+     
+	# If snake crosses the boundaries, make it enter from the other side
+	if snake[0][0] == 0: snake[0][0] = 18
+	if snake[0][1] == 0: snake[0][1] = 58
+	if snake[0][0] == 19: snake[0][0] = 1
+	if snake[0][1] == 59: snake[0][1] = 1
+     
+	# Exit if snake crosses the boundaries (Uncomment to enable)
+	#if snake[0][0] == 0 or snake[0][0] == 19 or snake[0][1] == 0 or snake[0][1] == 59: break
+     
+	# If snake runs over itself
+	if snake[0] in snake[1:]: break
+
+	timeout = 0.001 * (150 - (len(snake)/5 + len(snake)/10)%120)   # Increases the speed of Snake as its length increases
+	prevKey = key                                                  # Previous key pressed
+	lock.release()
+	vim.defer('Update')
+	sleep(timeout)
+    vim.defer('End')
+EOF
+ 
+function Update()
+python << EOF
+lock.acquire()
+if snake[0] == food:                                            # When snake eats the food
+    food = []
+    score += 1
+    while food == []:
+	food = [randint(1, 18), randint(1, 58)]                 # Calculating next food's coordinates
+	if food in snake: food = []
+    addstr(food[0], food[1], '*')
+else:    
+    last = snake.pop()                                          # [1] If it does not eat the food, length decreases
+    addstr(last[0], last[1], ' ')
+addstr(snake[0][0], snake[0][1], '#')
+addstr(0, 2, 'Score : ' + str(score) + ' ')                    # Printing 'Score' and
+addstr(0, 27, ' SNAKE / MOVEMENTS(hjkl) EXIT(i) PAUSE(space) ')
+lock.release()
+EOF
+endfunction
+
+function End()
+unmap k
+unmap j
+unmap h
+unmap l
+unmap i
+unmap <space>
+python << EOF
+buf[:] = None
+buf.append("Score - " + str(score))
+buf.append("http://bitemelater.in")
+EOF
+endfunction
+
+function KeyPress(k)
+python << EOF
+lock.acquire()
+key = vim.eval('a:k')
+lock.release()
+EOF
+endfunction
+
+nnoremap k :call KeyPress('up')<cr>
+nnoremap j :call KeyPress('down')<cr>
+nnoremap h :call KeyPress('left')<cr>
+nnoremap l :call KeyPress('right')<cr>
+nnoremap i :call KeyPress('esc')<cr>
+nnoremap <space> :call KeyPress('space')<cr>
+
+python << EOF
+game = Thread(target=run_game)
+game.daemon = True
+game.start()
+EOF
diff --git a/src/Makefile b/src/Makefile
index bceb65c..aa9f445 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1449,6 +1449,7 @@ BASIC_SRC = \
 	memline.c \
 	menu.c \
 	message.c \
+	message_queue.c \
 	misc1.c \
 	misc2.c \
 	move.c \
@@ -1537,6 +1538,7 @@ OBJ_COMMON = \
         objects/memline.o \
 	objects/menu.o \
 	objects/message.o \
+	objects/message_queue.o \
 	objects/misc1.o \
 	objects/misc2.o \
 	objects/move.o \
@@ -2646,6 +2648,9 @@ objects/menu.o: menu.c
 objects/message.o: message.c
 	$(CCC) -o $@ message.c
 
+objects/message_queue.o: message_queue.c
+	$(CCC) -o $@ message_queue.c
+
 objects/misc1.o: misc1.c
 	$(CCC) -o $@ misc1.c
 
diff --git a/src/config.h.in b/src/config.h.in
index a4e216b..e473d08 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -323,6 +323,9 @@
 /* Define for linking via dlopen() or LoadLibrary() */
 #undef DYNAMIC_LUA
 
+/* Define if you want to use thread-safe message queue. */
+#undef FEAT_MESSAGEQUEUE
+
 /* Define if you want to include the MzScheme interpreter. */
 #undef FEAT_MZSCHEME
 
diff --git a/src/configure.in b/src/configure.in
index 2718d31..8e52d21 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -2801,7 +2801,6 @@ AC_CHECK_HEADERS(sys/sysctl.h, [], [],
 #  include <sys/param.h>
 #endif])
 
-
 dnl pthread_np.h may exist but can only be used after including pthread.h
 AC_MSG_CHECKING([for pthread_np.h])
 AC_TRY_COMPILE([
@@ -2812,6 +2811,22 @@ AC_TRY_COMPILE([
 		      AC_DEFINE(HAVE_PTHREAD_NP_H),
 	      AC_MSG_RESULT(no))
 
+AC_ARG_ENABLE(messagequeue,
+	[  --enable-messagequeue[=OPTS]   Configure vim to use a message queue for synchronization with multi-threaded code. [default=yes] [OPTS=no/yes]], ,
+	[enable_messagequeue="yes"])
+AC_MSG_RESULT($enable_messagequeue)
+
+if test "$enable_messagequeue" = "yes"; then
+    # pthread is needed for the message queue
+    AC_MSG_CHECKING([for pthread.h])
+    AC_TRY_COMPILE([
+    #include <pthread.h>],
+			  [int i; i = 0;],
+		  AC_MSG_RESULT(yes)
+			  AC_DEFINE(FEAT_MESSAGEQUEUE),
+		  AC_MSG_RESULT(no))
+fi
+
 AC_CHECK_HEADERS(strings.h)
 if test "x$MACOSX" = "xyes"; then
   dnl The strings.h file on OS/X contains a warning and nothing useful.
diff --git a/src/getchar.c b/src/getchar.c
index fe6423d..dc42929 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -1335,11 +1335,11 @@ save_typebuf()
     return OK;
 }
 
-static int old_char = -1;	/* character put back by vungetc() */
-static int old_mod_mask;	/* mod_mask for ungotten character */
+int old_char = -1;	/* character put back by vungetc() */
+int old_mod_mask;	/* mod_mask for ungotten character */
 #ifdef FEAT_MOUSE
-static int old_mouse_row;	/* mouse_row related to old_char */
-static int old_mouse_col;	/* mouse_col related to old_char */
+int old_mouse_row;	/* mouse_row related to old_char */
+int old_mouse_col;	/* mouse_col related to old_char */
 #endif
 
 #if defined(FEAT_EVAL) || defined(FEAT_EX_EXTRA) || defined(PROTO)
diff --git a/src/globals.h b/src/globals.h
index 916f7e3..8d7e6d3 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -82,6 +82,8 @@ EXTERN int	screen_Columns INIT(= 0);   /* actual size of ScreenLines[] */
  * held down based on the MOD_MASK_* symbols that are read first.
  */
 EXTERN int	mod_mask INIT(= 0x0);		/* current key modifiers */
+EXTERN int	old_mod_mask;	/* mod_mask for ungotten character */
+EXTERN int	old_char;	/* ungotten character */
 
 /*
  * Cmdline_row is the row where the command line starts, just below the
@@ -394,6 +396,9 @@ EXTERN buf_T	*au_new_curbuf INIT(= NULL);
  */
 EXTERN int	mouse_row;
 EXTERN int	mouse_col;
+EXTERN int	old_mouse_row;	/* mouse_row related to old_char */
+EXTERN int	old_mouse_col;	/* mouse_col related to old_char */
+
 EXTERN int	mouse_past_bottom INIT(= FALSE);/* mouse below last line */
 EXTERN int	mouse_past_eol INIT(= FALSE);	/* mouse right of line */
 EXTERN int	mouse_dragging INIT(= 0);	/* extending Visual area with
diff --git a/src/gui.c b/src/gui.c
index b667ba3..5b3d6f9 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -87,6 +87,10 @@ gui_start()
 
     ++recursive;
 
+#if defined(FEAT_MESSAGEQUEUE) && defined(FEAT_GUI_X11)
+    if (recursive <= 1) XInitThreads();
+#endif
+
 #ifdef MAY_FORK
     /*
      * Quit the current process and continue in the child.
diff --git a/src/if_py_both.h b/src/if_py_both.h
index 17c02a9..ee1d1c1 100644
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -798,6 +798,27 @@ VimToPython(typval_T *our_tv, int depth, PyObject *lookup_dict)
 }
 
     static PyObject *
+VimDefer(PyObject *self UNUSED, PyObject *args)
+{
+    char_u	*func;
+    PyObject	*string;
+    PyObject	*todecref;
+
+    if (!PyArg_ParseTuple(args, "O", &string))
+	return NULL;
+
+    if (!(func = StringToChars(string, &todecref)))
+	return NULL;
+
+    queue_push(DeferredCall, strdup(func));
+
+    Py_XDECREF(todecref);
+    Py_INCREF(Py_None);
+
+    return Py_None;
+}
+
+    static PyObject *
 VimEval(PyObject *self UNUSED, PyObject *args)
 {
     char_u	*expr;
@@ -1287,6 +1308,7 @@ VimPathHook(PyObject *self UNUSED, PyObject *args)
 static struct PyMethodDef VimMethods[] = {
     /* name,	    function,			calling,			documentation */
     {"command",	    VimCommand,			METH_O,				"Execute a Vim ex-mode command" },
+    {"defer",	    VimDefer,			METH_VARARGS,			"Call a vim function in the next message loop iteration" },
     {"eval",	    VimEval,			METH_VARARGS,			"Evaluate an expression using Vim evaluator" },
     {"bindeval",    VimEvalPy,			METH_O,				"Like eval(), but returns objects attached to vim ones"},
     {"strwidth",    VimStrwidth,		METH_O,				"Screen string width, counts <Tab> as having width 1"},
diff --git a/src/main.c b/src/main.c
index 0407795..fc77ebb 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1043,6 +1043,16 @@ main_loop(cmdwin, noexmode)
     linenr_T	conceal_new_cursor_line = 0;
     int		conceal_update_lines = FALSE;
 #endif
+#ifdef FEAT_MESSAGEQUEUE
+    input_data_T    *id;    /* Input data read from the other thread */
+    message_T	    *msg;   /* next message */
+    typval_T	    *defer_tv;
+#endif
+
+#ifdef FEAT_MESSAGEQUEUE
+    /* Initialize the message queue */
+    queue_init();
+#endif
 
 #if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
     /* Setup to catch a terminating error from the X server.  Just ignore
@@ -1326,7 +1336,54 @@ main_loop(cmdwin, noexmode)
 	    do_exmode(exmode_active == EXMODE_VIM);
 	}
 	else
+	{
+#ifdef FEAT_MESSAGEQUEUE
+	    /* Notify the background thread that it should read some input */
+	    input_notify();
+
+	    /* 
+	     * Wait for a message, which can be an 'UserInput' message
+	     * set by the background thread or a 'DeferredCall' message
+	     * indirectly set by vimscript.
+	     */
+	    msg = queue_shift();
+
+	    switch (msg->type)
+	    {
+	    case UserInput:
+		id = (input_data_T *)msg->data;
+
+		/* 
+		 * Trick vgetc into thinking this char was returned by
+		 * vungetc. Its hacky but avoids messing with that 
+		 * function for now.
+		 */
+		old_char = id->character;
+		old_mod_mask = id->mod_mask;
+		old_mouse_row = id->mouse_row;
+		old_mouse_col = id->mouse_col;
+
+		/* Run the normal command */
+		normal_cmd(&oa, TRUE);
+		break;
+	    case DeferredCall:
+		/* Call the defered function */
+		(void)call_func_retnr((char_u *)msg->data, 0, 0, FALSE);
+		/* 
+		 * Force a redraw in case the called function updated
+		 * something.
+		 */
+		shell_resized();
+		break;
+	    }
+	    /* Free memory we no longer need */
+	    vim_free(msg->data);
+	    vim_free(msg);
+#else
+	    /* Run the normal command */
 	    normal_cmd(&oa, TRUE);
+#endif
+	}
     }
 }
 
diff --git a/src/message_queue.c b/src/message_queue.c
new file mode 100644
index 0000000..4c7bc8c
--- /dev/null
+++ b/src/message_queue.c
@@ -0,0 +1,226 @@
+#include "vim.h"
+
+#ifdef FEAT_MESSAGEQUEUE
+
+#include <pthread.h>
+
+#include "message_queue.h"
+
+typedef struct message_queue_T
+{
+    pthread_mutex_t	mutex;
+    pthread_cond_t	cond;
+    message_T		*head;
+    message_T		*tail;
+} message_queue_T;
+
+message_queue_T	    message_queue;
+
+int		    waiting_for_input;
+pthread_t	    input_thread;
+pthread_mutex_t	    input_mutex;
+pthread_cond_t	    input_cond;
+
+/* 
+ * FIXME: Figure out the right way to deal with such errors by asking
+ * on the list
+ */
+    void
+pthread_error(const char *msg)
+{
+    fprintf(stderr, "%s\n", msg);
+    mch_exit(EXIT_FAILURE);
+}
+
+/*
+ * Private helpers to used to lock/unlock the input mutex
+ */
+    static void
+lock(pthread_mutex_t *mutex)
+{
+    if (pthread_mutex_lock(mutex) != 0)
+	pthread_error("Error acquiring user input lock");
+}
+
+    static void
+unlock(pthread_mutex_t *mutex)
+{
+    if (pthread_mutex_unlock(mutex) != 0)
+	pthread_error("Error releasing user input lock");
+}
+
+/*
+ * Function used by the background thread to wait for a signal to read
+ * input from the main thread
+ */
+    void
+input_wait()
+{
+    lock(&input_mutex);
+
+    if (pthread_cond_wait(&input_cond, &input_mutex) != 0)
+	pthread_error("Failed to wait for condition");
+}
+
+
+/*
+ * Function used by the main thread to notify that it should read something
+ */
+    void
+input_notify()
+{
+    if (waiting_for_input)
+	return;
+
+    lock(&input_mutex);
+
+    if (pthread_cond_broadcast(&input_cond) != 0)
+	pthread_error("Failed to acquire lock");
+
+    unlock(&input_mutex);
+}
+
+/*
+ * This function will listen for user input in a separate thread, but only
+ * when asked by the main thead
+ */
+    void *
+vgetcs(arg)
+    void	*arg UNUSED; /* Unsused thread start argument */
+{
+
+    input_data_T *data;
+
+    while (TRUE)
+    {
+	waiting_for_input = FALSE;
+
+	// Only try to read input when asked by the main thread
+	input_wait();
+
+	// Dont let the main thread call 'input_notify' or else it would block
+	waiting_for_input = TRUE;
+
+	// Allocate space to hold input data
+	data = (input_data_T *)alloc(sizeof(input_data_T));
+
+	/* The input mutex was configured to be reentrant, so we lock */
+	/* it before entering vgetc. This way we can safely */
+	/* retrieve other global variables set by it (mod_mask, mouse{row,
+	 * col}) without risking the main thread overriding them */ 
+	data->character = vgetc();
+	data->mod_mask = mod_mask;
+	data->mouse_row = mouse_row;
+	data->mouse_col = mouse_col;
+
+	unlock(&input_mutex);
+	queue_push(UserInput, data);
+    }
+}
+
+/*
+ * Initialize the message queue and start listening for user input in a
+ * separate thread.
+ */
+    void
+queue_init()
+{
+    pthread_attr_t attr;
+
+    if (pthread_mutex_init(&input_mutex, NULL) != 0)
+	pthread_error("Failed to init the mutex");
+
+    if (pthread_cond_init(&input_cond, NULL) != 0)
+	pthread_error("Failed to init the condition");
+
+    if (pthread_mutex_init(&message_queue.mutex, NULL) != 0)
+	pthread_error("Failed to init the mutex");
+
+    if (pthread_cond_init(&message_queue.cond, NULL) != 0)
+	pthread_error("Failed to init the condition");
+
+
+    message_queue.head = NULL;
+    message_queue.tail = NULL;
+
+    if (pthread_attr_init(&attr) != 0)
+	pthread_error("Failed to initialize the thread attribute");
+
+    if (pthread_create(&input_thread, &attr, &vgetcs, NULL) != 0)
+	pthread_error("Failed to initialize the user input thread");
+}
+
+/* 
+ * Insert a message at the end of the queue.
+ */
+    void
+queue_push(type, data)
+    MessageType	    type;    /* Type of message */
+    void	    *data;   /* Data associated with the message */
+{
+    int empty;
+    message_T *msg = (message_T *)alloc(sizeof(message_T));
+    msg->type = type;
+    msg->data = data;
+    msg->next = NULL;
+
+    /* Acquire queue lock */
+    lock(&message_queue.mutex);
+
+    empty = message_queue.head == NULL;
+
+    if (empty) {
+	/* Put the message at the beginning for immediate consumption */
+	msg->next = message_queue.head;
+	message_queue.head = msg;
+
+	/*
+	 * Queue was empty and consequently the main thread was waiting,
+	 * so wake it up to continue after the lock is released
+	 */
+	if (empty && pthread_cond_broadcast(&message_queue.cond) != 0)
+	    pthread_error("Failed to wake the main thread");
+
+    } else {
+	/* 
+	 * There are pending messages, put this one at the end, adjusting the
+	 * next pointer.
+	 */
+	if (message_queue.tail == NULL) {
+	    message_queue.head->next = msg;
+	} else {
+	    message_queue.tail->next = msg;
+	}
+	message_queue.tail = msg;
+    }
+
+    unlock(&message_queue.mutex);
+}
+
+/* Take a message from the beginning of the queue */
+    message_T *
+queue_shift()
+{
+    message_T *rv;
+
+    lock(&message_queue.mutex);
+
+    if (message_queue.head == NULL) {
+	/* 
+	 * Queue is empty, temporarily release the lock and wait for
+	 * more messages
+	 */
+	if (pthread_cond_wait(&message_queue.cond,
+		    &message_queue.mutex) != 0)
+	    pthread_error("Failed to wait for condition");
+    }
+
+    rv = message_queue.head;
+    message_queue.head = rv->next;
+
+    unlock(&message_queue.mutex);
+
+    return rv;
+}
+
+#endif
diff --git a/src/message_queue.h b/src/message_queue.h
new file mode 100644
index 0000000..3b25d03
--- /dev/null
+++ b/src/message_queue.h
@@ -0,0 +1,34 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved	by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+#ifndef MESSAGE_QUEUE_H
+#define MESSAGE_QUEUE_H
+
+typedef enum { UserInput, DeferredCall } MessageType;
+
+typedef struct message_T
+{ 
+  struct message_T * next;
+  MessageType type;
+  void *data;
+} message_T;
+
+typedef struct input_data_T
+{
+    int character;
+    int mod_mask;
+    int mouse_row;
+    int mouse_col;
+} input_data_T;
+
+void input_notify();
+void queue_init();
+void queue_push(MessageType, void *);
+message_T * queue_shift();
+
+#endif
diff --git a/src/version.c b/src/version.c
index 1fd98ea..e782d1d 100644
--- a/src/version.c
+++ b/src/version.c
@@ -335,6 +335,11 @@ static char *(features[]) =
 #else
 	"-menu",
 #endif
+#ifdef FEAT_MESSAGEQUEUE
+	"+messagequeue",
+#else
+	"-messagequeue",
+#endif
 #ifdef FEAT_SESSION
 	"+mksession",
 #else
diff --git a/src/vim.h b/src/vim.h
index 88f3dc2..678eaa0 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1978,6 +1978,10 @@ typedef int VimClipboard;	/* This is required for the prototypes. */
 
 #include "globals.h"	    /* global variables and messages */
 
+#ifdef FEAT_MESSAGEQUEUE
+# include "message_queue.h"  /* message queue API */
+#endif
+
 #ifdef FEAT_SNIFF
 # include "if_sniff.h"
 #endif
