You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pb...@apache.org on 2012/10/06 05:59:38 UTC
svn commit: r1394915 [5/7] - in /subversion/branches/inheritable-props: ./
build/ build/ac-macros/ subversion/bindings/swig/
subversion/bindings/swig/include/
subversion/bindings/swig/perl/libsvn_swig_perl/
subversion/bindings/swig/perl/native/ subvers...
Modified: subversion/branches/inheritable-props/subversion/libsvn_ra_svn/marshal.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_ra_svn/marshal.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_ra_svn/marshal.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_ra_svn/marshal.c Sat Oct 6 03:59:33 2012
@@ -54,15 +54,34 @@
#define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
+/* When zero copy has been enabled, don't use blocking writes. Instead,
+ * time out after this many microseconds. */
+
+#define ZERO_COPY_TIMEOUT 1000000
+
+/* Return the APR socket timeout to be used for the connection depending
+ * on whether there is a blockage handler or zero copy has been activated. */
+static apr_interval_time_t
+get_timeout(svn_ra_svn_conn_t *conn)
+{
+ return conn->block_handler ? 0
+ : (conn->zero_copy_limit ? ZERO_COPY_TIMEOUT
+ : -1);
+}
+
/* --- CONNECTION INITIALIZATION --- */
-svn_ra_svn_conn_t *svn_ra_svn_create_conn2(apr_socket_t *sock,
+svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock,
apr_file_t *in_file,
apr_file_t *out_file,
int compression_level,
+ apr_size_t zero_copy_limit,
+ apr_size_t error_check_interval,
apr_pool_t *pool)
{
- svn_ra_svn_conn_t *conn = apr_palloc(pool, sizeof(*conn));
+ svn_ra_svn_conn_t *conn;
+ void *mem = apr_palloc(pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
+ conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
assert((sock && !in_file && !out_file) || (!sock && in_file && out_file));
#ifdef SVN_HAVE_SASL
@@ -73,10 +92,14 @@ svn_ra_svn_conn_t *svn_ra_svn_create_con
conn->read_ptr = conn->read_buf;
conn->read_end = conn->read_buf;
conn->write_pos = 0;
+ conn->written_since_error_check = 0;
+ conn->error_check_interval = error_check_interval;
+ conn->may_check_for_error = error_check_interval == 0;
conn->block_handler = NULL;
conn->block_baton = NULL;
conn->capabilities = apr_hash_make(pool);
conn->compression_level = compression_level;
+ conn->zero_copy_limit = zero_copy_limit;
conn->pool = pool;
if (sock != NULL)
@@ -86,6 +109,7 @@ svn_ra_svn_conn_t *svn_ra_svn_create_con
if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
&& apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
conn->remote_ip = NULL;
+ svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
}
else
{
@@ -96,14 +120,25 @@ svn_ra_svn_conn_t *svn_ra_svn_create_con
return conn;
}
+svn_ra_svn_conn_t *svn_ra_svn_create_conn2(apr_socket_t *sock,
+ apr_file_t *in_file,
+ apr_file_t *out_file,
+ int compression_level,
+ apr_pool_t *pool)
+{
+ return svn_ra_svn_create_conn3(sock, in_file, out_file,
+ compression_level, 0, 0, pool);
+}
+
/* backward-compatible implementation using the default compression level */
svn_ra_svn_conn_t *svn_ra_svn_create_conn(apr_socket_t *sock,
apr_file_t *in_file,
apr_file_t *out_file,
apr_pool_t *pool)
{
- return svn_ra_svn_create_conn2(sock, in_file, out_file,
- SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
+ return svn_ra_svn_create_conn3(sock, in_file, out_file,
+ SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0,
+ pool);
}
svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
@@ -146,6 +181,12 @@ svn_ra_svn_compression_level(svn_ra_svn_
return conn->compression_level;
}
+apr_size_t
+svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
+{
+ return conn->zero_copy_limit;
+}
+
const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
{
return conn->remote_ip;
@@ -156,11 +197,9 @@ svn_ra_svn__set_block_handler(svn_ra_svn
ra_svn_block_handler_t handler,
void *baton)
{
- apr_interval_time_t interval = (handler) ? 0 : -1;
-
conn->block_handler = handler;
conn->block_baton = baton;
- svn_ra_svn__stream_timeout(conn->stream, interval);
+ svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
}
svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn,
@@ -171,20 +210,6 @@ svn_boolean_t svn_ra_svn__input_waiting(
/* --- WRITE BUFFER MANAGEMENT --- */
-/* Write bytes into the write buffer until either the write buffer is
- * full or we reach END. */
-static const char *writebuf_push(svn_ra_svn_conn_t *conn, const char *data,
- const char *end)
-{
- apr_ssize_t buflen, copylen;
-
- buflen = sizeof(conn->write_buf) - conn->write_pos;
- copylen = (buflen < end - data) ? buflen : end - data;
- memcpy(conn->write_buf + conn->write_pos, data, copylen);
- conn->write_pos += copylen;
- return data + copylen;
-}
-
/* Write data to socket or output file as appropriate. */
static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
const char *data, apr_size_t len)
@@ -223,6 +248,10 @@ static svn_error_t *writebuf_output(svn_
}
}
+ conn->written_since_error_check += len;
+ conn->may_check_for_error
+ = conn->written_since_error_check >= conn->error_check_interval;
+
if (subpool)
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
@@ -242,19 +271,23 @@ static svn_error_t *writebuf_flush(svn_r
static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
const char *data, apr_size_t len)
{
- const char *end = data + len;
-
- if (conn->write_pos > 0 && conn->write_pos + len > sizeof(conn->write_buf))
+ /* data >= 8k is sent immediately */
+ if (len >= sizeof(conn->write_buf) / 2)
{
- /* Fill and then empty the write buffer. */
- data = writebuf_push(conn, data, end);
- SVN_ERR(writebuf_flush(conn, pool));
+ if (conn->write_pos > 0)
+ SVN_ERR(writebuf_flush(conn, pool));
+
+ return writebuf_output(conn, pool, data, len);
}
- if (end - data > (apr_ssize_t)sizeof(conn->write_buf))
- SVN_ERR(writebuf_output(conn, pool, data, end - data));
- else
- writebuf_push(conn, data, end);
+ /* ensure room for the data to add */
+ if (conn->write_pos + len > sizeof(conn->write_buf))
+ SVN_ERR(writebuf_flush(conn, pool));
+
+ /* buffer the new data block as well */
+ memcpy(conn->write_buf + conn->write_pos, data, len);
+ conn->write_pos += len;
+
return SVN_NO_ERROR;
}
@@ -331,6 +364,31 @@ static svn_error_t *readbuf_input(svn_ra
return SVN_NO_ERROR;
}
+/* Treat the next LEN input bytes from CONN as "read" */
+static svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_size_t len)
+{
+ do
+ {
+ apr_size_t buflen = conn->read_end - conn->read_ptr;
+ apr_size_t copylen = (buflen < len) ? buflen : len;
+ conn->read_ptr += copylen;
+ len -= copylen;
+ if (len == 0)
+ break;
+
+ buflen = sizeof(conn->read_buf);
+ SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
+ if (buflen == 0)
+ return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
+
+ conn->read_end = conn->read_buf + buflen;
+ conn->read_ptr = conn->read_buf;
+ }
+ while (len > 0);
+
+ return SVN_NO_ERROR;
+}
+
/* Read data from the socket into the read buffer, which must be empty. */
static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
{
@@ -562,13 +620,557 @@ svn_error_t *svn_ra_svn_flush(svn_ra_svn
/* --- WRITING TUPLES --- */
+static svn_error_t *
+vwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ const char *cstr = va_arg(*ap, const char *);
+ SVN_ERR_ASSERT(cstr);
+ return svn_ra_svn_write_cstring(conn, pool, cstr);
+}
+
+static svn_error_t *
+vwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ const char *cstr = va_arg(*ap, const char *);
+ return cstr ? svn_ra_svn_write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ const svn_string_t *str = va_arg(*ap, const svn_string_t *);
+ SVN_ERR_ASSERT(str);
+ return svn_ra_svn_write_string(conn, pool, str);
+}
+
+static svn_error_t *
+vwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ const svn_string_t *str = va_arg(*ap, const svn_string_t *);
+ return str ? svn_ra_svn_write_string(conn, pool, str) : SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_tuple_start_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ return svn_ra_svn_start_list(conn, pool);
+}
+
+static svn_error_t *
+vwrite_tuple_end_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ return svn_ra_svn_end_list(conn, pool);
+}
+
+static svn_error_t *
+vwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ const char *cstr = va_arg(*ap, const char *);
+ SVN_ERR_ASSERT(cstr);
+ return svn_ra_svn_write_word(conn, pool, cstr);
+}
+
+static svn_error_t *
+vwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ const char *cstr = va_arg(*ap, const char *);
+ return cstr ? svn_ra_svn_write_word(conn, pool, cstr) : SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
+ return svn_ra_svn_write_number(conn, pool, rev);
+}
+
+static svn_error_t *
+vwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
+ return SVN_IS_VALID_REVNUM(rev)
+ ? svn_ra_svn_write_number(conn, pool, rev)
+ : SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ return svn_ra_svn_write_number(conn, pool, va_arg(*ap, apr_uint64_t));
+}
+
+static svn_error_t *
+vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ const char *cstr = va_arg(*ap, svn_boolean_t) ? "true" : "false";
+ return svn_ra_svn_write_word(conn, pool, cstr);
+}
+
+static svn_error_t *
+vwrite_cmd_open_root(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_delete_entry(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_add_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_open_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_change_dir_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_string_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_absent_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_add_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_open_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_change_file_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_string_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_close_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_absent_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_string(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_apply_textdelta(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_no_op(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_word(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_word(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_string_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_string_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_change_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_string_opt(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_word(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_switch(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_word(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_word(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_word(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_stat(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_cstring_opt(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_word(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+vwrite_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ SVN_ERR(vwrite_tuple_cstring(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+ SVN_ERR(vwrite_tuple_revision(conn, pool, ap));
+
+ return SVN_NO_ERROR;
+}
+
+
+typedef svn_error_t *
+(*vwrite_tuple_func)(svn_ra_svn_conn_t *, apr_pool_t *, va_list *);
+
+typedef struct cmd_template_t
+{
+ const char *start_sequence;
+ apr_size_t start_sequence_length;
+ vwrite_tuple_func write_ops;
+} cmd_template_t;
+
+static const cmd_template_t cmd_templates[svn_ra_svn_cmd__last]
+ = { {"( target-rev ( " , 15, vwrite_tuple_revision },
+ {"( open-root ( " , 14, vwrite_cmd_open_root },
+ {"( delete-entry ( " , 17, vwrite_cmd_delete_entry },
+ {"( add-dir ( " , 12, vwrite_cmd_add_dir },
+ {"( open-dir ( " , 13, vwrite_cmd_open_dir },
+ {"( change-dir-prop ( " , 20, vwrite_cmd_change_dir_prop },
+ {"( close-dir ( " , 14, vwrite_tuple_cstring },
+ {"( absent-dir ( " , 15, vwrite_cmd_absent_dir },
+ {"( add-file ( " , 13, vwrite_cmd_add_file },
+ {"( open-file ( " , 14, vwrite_cmd_open_file },
+ {"( change-file-prop ( ", 21, vwrite_cmd_change_file_prop },
+ {"( close-file ( " , 15, vwrite_cmd_close_file },
+ {"( absent-file ( " , 16, vwrite_cmd_absent_file },
+ {"( textdelta-chunk ( " , 20, vwrite_cmd_textdelta_chunk },
+ {"( textdelta-end ( " , 18, vwrite_tuple_cstring },
+ {"( apply-textdelta ( " , 20, vwrite_cmd_apply_textdelta },
+ {"( close-edit ( " , 15, vwrite_cmd_no_op },
+ {"( abort-edit ( " , 15, vwrite_cmd_no_op },
+
+ {"( set-path ( " , 13, vwrite_cmd_set_path },
+ {"( delete-path ( " , 16, vwrite_tuple_cstring },
+ {"( link-path ( " , 14, vwrite_cmd_link_path },
+ {"( finish-report ( " , 18, vwrite_cmd_no_op },
+ {"( abort-report ( " , 17, vwrite_cmd_no_op },
+
+ {"( reparent ( " , 13, vwrite_tuple_cstring },
+ {"( get-latest-rev ( " , 19, vwrite_cmd_no_op },
+ {"( get-dated-rev ( " , 18, vwrite_tuple_cstring },
+ {"( change-rev-prop2 ( ", 21, vwrite_cmd_change_rev_prop2 },
+ {"( change-rev-prop ( " , 20, vwrite_cmd_change_rev_prop },
+ {"( rev-proplist ( " , 17, vwrite_tuple_revision },
+ {"( rev-prop ( " , 13, vwrite_cmd_rev_prop },
+ {"( get-file ( " , 13, vwrite_cmd_get_file },
+ {"( update ( " , 11, vwrite_cmd_update },
+ {"( switch ( " , 11, vwrite_cmd_switch },
+ {"( status ( " , 11, vwrite_cmd_status },
+ {"( diff ( " , 9, vwrite_cmd_diff },
+ {"( check-path ( " , 15, vwrite_cmd_check_path },
+ {"( stat ( " , 9, vwrite_cmd_stat },
+ {"( get-file-revs ( " , 18, vwrite_cmd_get_file_revs },
+ {"( lock ( " , 9, vwrite_cmd_lock },
+ {"( unlock ( " , 11, vwrite_cmd_unlock },
+ {"( get-lock ( " , 13, vwrite_tuple_cstring },
+ {"( get-locks ( " , 14, vwrite_cmd_get_locks },
+ {"( replay ( " , 11, vwrite_cmd_replay },
+ {"( replay-range ( " , 17, vwrite_cmd_replay_range },
+ {"( get-deleted-rev ( " , 20, vwrite_cmd_get_deleted_rev }
+ };
+
+
+
+
static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
- const char *fmt, va_list ap)
+ const char *fmt, va_list *ap)
{
svn_boolean_t opt = FALSE;
- svn_revnum_t rev;
- const char *cstr;
- const svn_string_t *str;
if (*fmt == '!')
fmt++;
@@ -577,53 +1179,30 @@ static svn_error_t *vwrite_tuple(svn_ra_
for (; *fmt; fmt++)
{
if (*fmt == 'c')
- {
- cstr = va_arg(ap, const char *);
- if (cstr)
- SVN_ERR(svn_ra_svn_write_cstring(conn, pool, cstr));
- else
- SVN_ERR_ASSERT(opt);
- }
+ SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap)
+ : vwrite_tuple_cstring(conn, pool, ap));
else if (*fmt == 's')
- {
- str = va_arg(ap, const svn_string_t *);
- if (str)
- SVN_ERR(svn_ra_svn_write_string(conn, pool, str));
- else
- SVN_ERR_ASSERT(opt);
- }
+ SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
+ : vwrite_tuple_string(conn, pool, ap));
else if (*fmt == '(' && !opt)
- SVN_ERR(svn_ra_svn_start_list(conn, pool));
+ SVN_ERR(vwrite_tuple_start_list(conn, pool, ap));
else if (*fmt == ')')
{
- SVN_ERR(svn_ra_svn_end_list(conn, pool));
+ SVN_ERR(vwrite_tuple_end_list(conn, pool, ap));
opt = FALSE;
}
else if (*fmt == '?')
opt = TRUE;
else if (*fmt == 'w')
- {
- cstr = va_arg(ap, const char *);
- if (cstr)
- SVN_ERR(svn_ra_svn_write_word(conn, pool, cstr));
- else
- SVN_ERR_ASSERT(opt);
- }
+ SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap)
+ : vwrite_tuple_word(conn, pool, ap));
else if (*fmt == 'r')
- {
- rev = va_arg(ap, svn_revnum_t);
- if (SVN_IS_VALID_REVNUM(rev))
- SVN_ERR(svn_ra_svn_write_number(conn, pool, rev));
- else
- SVN_ERR_ASSERT(opt);
- }
+ SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
+ : vwrite_tuple_revision(conn, pool, ap));
else if (*fmt == 'n' && !opt)
- SVN_ERR(svn_ra_svn_write_number(conn, pool, va_arg(ap, apr_uint64_t)));
+ SVN_ERR(vwrite_tuple_number(conn, pool, ap));
else if (*fmt == 'b' && !opt)
- {
- cstr = va_arg(ap, svn_boolean_t) ? "true" : "false";
- SVN_ERR(svn_ra_svn_write_word(conn, pool, cstr));
- }
+ SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
else if (*fmt == '!' && !*(fmt + 1))
return SVN_NO_ERROR;
else
@@ -640,7 +1219,7 @@ svn_error_t *svn_ra_svn_write_tuple(svn_
va_list ap;
va_start(ap, fmt);
- err = vwrite_tuple(conn, pool, fmt, ap);
+ err = vwrite_tuple(conn, pool, fmt, &ap);
va_end(ap);
return err;
}
@@ -800,6 +1379,95 @@ static svn_error_t *read_item(svn_ra_svn
return SVN_NO_ERROR;
}
+/* Given the first non-whitespace character FIRST_CHAR, read the first
+ * command (word) encountered in CONN into *ITEM. If ITEM is NULL, skip
+ * to the end of the current list. Use POOL for allocations. */
+static svn_error_t *
+read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
+ const char **item, char first_char)
+{
+ char c = first_char;
+
+ /* Determine the item type and read it in. Make sure that c is the
+ * first character at the end of the item so we can test to make
+ * sure it's whitespace. */
+ if (svn_ctype_isdigit(c))
+ {
+ /* It's a number or a string. Read the number part, either way. */
+ apr_uint64_t val, prev_val=0;
+ val = c - '0';
+ while (1)
+ {
+ prev_val = val;
+ SVN_ERR(readbuf_getchar(conn, pool, &c));
+ if (!svn_ctype_isdigit(c))
+ break;
+ val = val * 10 + (c - '0');
+ if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
+ return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+ _("Number is larger than maximum"));
+ }
+ if (c == ':')
+ {
+ /* It's a string. */
+ SVN_ERR(readbuf_skip(conn, val));
+ SVN_ERR(readbuf_getchar(conn, pool, &c));
+ }
+ }
+ else if (svn_ctype_isalpha(c))
+ {
+ /* It's a word. */
+ if (item)
+ {
+ /* This is the word we want to read */
+
+ char *buf = apr_palloc(pool, 32);
+ apr_size_t len = 1;
+ buf[0] = c;
+
+ while (1)
+ {
+ SVN_ERR(readbuf_getchar(conn, pool, &c));
+ if (!svn_ctype_isalnum(c) && c != '-')
+ break;
+ buf[len] = c;
+ if (++len == 32)
+ return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+ _("Word too long"));
+ }
+ buf[len] = 0;
+ *item = buf;
+ }
+ else
+ {
+ /* we don't need the actual word, just skip it */
+ do
+ {
+ SVN_ERR(readbuf_getchar(conn, pool, &c));
+ }
+ while (svn_ctype_isalnum(c) || c == '-');
+ }
+ }
+ else if (c == '(')
+ {
+ /* Read in the list items. */
+ while (1)
+ {
+ SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
+ if (c == ')')
+ break;
+
+ if (item && *item == NULL)
+ SVN_ERR(read_command_only(conn, pool, item, c));
+ else
+ SVN_ERR(read_command_only(conn, pool, NULL, c));
+ }
+ SVN_ERR(readbuf_getchar(conn, pool, &c));
+ }
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *svn_ra_svn_read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
svn_ra_svn_item_t **item)
{
@@ -948,6 +1616,18 @@ svn_error_t *svn_ra_svn_read_tuple(svn_r
return err;
}
+svn_error_t *svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const char **command)
+{
+ char c;
+ SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
+
+ *command = NULL;
+ return read_command_only(conn, pool, command, c);
+}
+
+
svn_error_t *svn_ra_svn_parse_proplist(const apr_array_header_t *list,
apr_pool_t *pool,
apr_hash_t **props)
@@ -1148,11 +1828,28 @@ svn_error_t *svn_ra_svn_write_cmd(svn_ra
SVN_ERR(svn_ra_svn_start_list(conn, pool));
SVN_ERR(svn_ra_svn_write_word(conn, pool, cmdname));
va_start(ap, fmt);
- err = vwrite_tuple(conn, pool, fmt, ap);
+ err = vwrite_tuple(conn, pool, fmt, &ap);
va_end(ap);
return err ? svn_error_trace(err) : svn_ra_svn_end_list(conn, pool);
}
+svn_error_t *svn_ra_svn_write_templated_cmd(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ svn_ra_svn_cmd_t cmd, ...)
+{
+ va_list ap;
+ svn_error_t *err;
+
+ SVN_ERR(writebuf_write_short_string(conn, pool,
+ cmd_templates[cmd].start_sequence,
+ cmd_templates[cmd].start_sequence_length));
+ va_start(ap, cmd);
+ err = cmd_templates[cmd].write_ops(conn, pool, &ap);
+ va_end(ap);
+
+ return err ? err : writebuf_write_short_string(conn, pool, ") ) ", 4);
+}
+
svn_error_t *svn_ra_svn_write_cmd_response(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *fmt, ...)
@@ -1162,7 +1859,7 @@ svn_error_t *svn_ra_svn_write_cmd_respon
SVN_ERR(writebuf_write_short_string(conn, pool, "( success ", 10));
va_start(ap, fmt);
- err = vwrite_tuple(conn, pool, fmt, ap);
+ err = vwrite_tuple(conn, pool, fmt, &ap);
va_end(ap);
return err ? svn_error_trace(err) : svn_ra_svn_end_list(conn, pool);
}
Modified: subversion/branches/inheritable-props/subversion/libsvn_ra_svn/ra_svn.h
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_ra_svn/ra_svn.h?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_ra_svn/ra_svn.h (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_ra_svn/ra_svn.h Sat Oct 6 03:59:33 2012
@@ -56,9 +56,13 @@ typedef svn_error_t *(*ra_svn_block_hand
apr_pool_t *pool,
void *baton);
+/* The default "user agent". */
+#define SVN_RA_SVN__DEFAULT_USERAGENT "SVN/" SVN_VER_NUMBER
+
/* The size of our per-connection read and write buffers. */
-#define SVN_RA_SVN__READBUF_SIZE (4*4096)
-#define SVN_RA_SVN__WRITEBUF_SIZE (4*4096)
+#define SVN_RA_SVN__PAGE_SIZE 4096
+#define SVN_RA_SVN__READBUF_SIZE (4 * SVN_RA_SVN__PAGE_SIZE)
+#define SVN_RA_SVN__WRITEBUF_SIZE (4 * SVN_RA_SVN__PAGE_SIZE)
/* Create forward reference */
typedef struct svn_ra_svn__session_baton_t svn_ra_svn__session_baton_t;
@@ -66,6 +70,14 @@ typedef struct svn_ra_svn__session_baton
/* This structure is opaque to the server. The client pokes at the
* first few fields during setup and cleanup. */
struct svn_ra_svn_conn_st {
+
+ /* I/O buffers */
+ char write_buf[SVN_RA_SVN__WRITEBUF_SIZE];
+ char read_buf[SVN_RA_SVN__READBUF_SIZE];
+ char *read_ptr;
+ char *read_end;
+ apr_size_t write_pos;
+
svn_ra_svn__stream_t *stream;
svn_ra_svn__session_baton_t *session;
#ifdef SVN_HAVE_SASL
@@ -75,19 +87,32 @@ struct svn_ra_svn_conn_st {
apr_socket_t *sock;
svn_boolean_t encrypted;
#endif
- char read_buf[SVN_RA_SVN__READBUF_SIZE];
- char *read_ptr;
- char *read_end;
- char write_buf[SVN_RA_SVN__WRITEBUF_SIZE];
- apr_size_t write_pos;
+
+ /* abortion check control */
+ apr_size_t written_since_error_check;
+ apr_size_t error_check_interval;
+ svn_boolean_t may_check_for_error;
+
+ /* repository info */
const char *uuid;
const char *repos_root;
+
+ /* TX block notification target */
ra_svn_block_handler_t block_handler;
void *block_baton;
+
+ /* server settings */
apr_hash_t *capabilities;
int compression_level;
+ apr_size_t zero_copy_limit;
+
+ /* who's on the other side of the connection? */
char *remote_ip;
+
+ /* EV2 support*/
svn_delta_shim_callbacks_t *shim_callbacks;
+
+ /* our pool */
apr_pool_t *pool;
};
@@ -104,6 +129,7 @@ struct svn_ra_svn__session_baton_t {
void *callbacks_baton;
apr_off_t bytes_read, bytes_written; /* apr_off_t's because that's what
the callback interface uses */
+ const char *useragent;
};
/* Set a callback for blocked writes on conn. This handler may
@@ -167,6 +193,13 @@ svn_error_t *svn_ra_svn__stream_write(sv
svn_error_t *svn_ra_svn__stream_read(svn_ra_svn__stream_t *stream,
char *data, apr_size_t *len);
+/* Read the command word from CONN, return it in *COMMAND and skip to the
+ * end of the command. Allocate data in POOL.
+ */
+svn_error_t *svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const char **command);
+
/* Set the timeout for operations on STREAM to INTERVAL. */
void svn_ra_svn__stream_timeout(svn_ra_svn__stream_t *stream,
apr_interval_time_t interval);
Modified: subversion/branches/inheritable-props/subversion/libsvn_repos/authz.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_repos/authz.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_repos/authz.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_repos/authz.c Sat Oct 6 03:59:33 2012
@@ -714,14 +714,15 @@ static svn_boolean_t authz_validate_sect
{
struct authz_validate_baton *b = baton;
- /* If the section is the groups definition, use the group checking
- callback. Otherwise, use the rule checking callback. */
- if (strncmp(name, "groups", 6) == 0)
+ /* Use the group checking callback for the "groups" section... */
+ if (strcmp(name, "groups") == 0)
svn_config_enumerate2(b->config, name, authz_validate_group,
baton, pool);
- else if (strncmp(name, "aliases", 7) == 0)
+ /* ...and the alias checking callback for "aliases"... */
+ else if (strcmp(name, "aliases") == 0)
svn_config_enumerate2(b->config, name, authz_validate_alias,
baton, pool);
+ /* ...but for everything else use the rule checking callback. */
else
{
/* Validate the section's name. Skip the optional REPOS_NAME. */
Modified: subversion/branches/inheritable-props/subversion/libsvn_repos/delta.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_repos/delta.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_repos/delta.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_repos/delta.c Sat Oct 6 03:59:33 2012
@@ -629,12 +629,23 @@ svn_repos__compare_files(svn_boolean_t *
if (!*changed_p)
return SVN_NO_ERROR;
- /* From this point on, assume things haven't changed. */
+ /* If the SHA1 checksums match for these things, we'll claim they
+ have the same contents. (We don't give quite as much weight to
+ MD5 checksums.) */
+ SVN_ERR(svn_fs_file_checksum(&checksum1, svn_checksum_sha1,
+ root1, path1, FALSE, pool));
+ SVN_ERR(svn_fs_file_checksum(&checksum2, svn_checksum_sha1,
+ root2, path2, FALSE, pool));
+ if (checksum1 && checksum2)
+ {
+ *changed_p = !svn_checksum_match(checksum1, checksum2);
+ return SVN_NO_ERROR;
+ }
+
+ /* From this point on, our default answer is "Nothing's changed". */
*changed_p = FALSE;
- /* So, things have changed. But we need to know if the two sets of
- file contents are actually different. If they have differing
- sizes, then we know they differ. */
+ /* Different filesizes means the contents are different. */
SVN_ERR(svn_fs_file_length(&size1, root1, path1, pool));
SVN_ERR(svn_fs_file_length(&size2, root2, path2, pool));
if (size1 != size2)
@@ -643,8 +654,7 @@ svn_repos__compare_files(svn_boolean_t *
return SVN_NO_ERROR;
}
- /* Same sizes, huh? Well, if their checksums differ, we know they
- differ. */
+ /* Different MD5 checksums means the contents are different. */
SVN_ERR(svn_fs_file_checksum(&checksum1, svn_checksum_md5, root1, path1,
FALSE, pool));
SVN_ERR(svn_fs_file_checksum(&checksum2, svn_checksum_md5, root2, path2,
@@ -655,13 +665,11 @@ svn_repos__compare_files(svn_boolean_t *
return SVN_NO_ERROR;
}
- /* Same sizes, same checksums. Chances are reallllly good that they
- don't differ, but to be absolute sure, we need to compare bytes. */
+ /* And finally, different contents means the ... uh ... contents are
+ different. */
SVN_ERR(svn_fs_file_contents(&stream1, root1, path1, pool));
SVN_ERR(svn_fs_file_contents(&stream2, root2, path2, pool));
-
SVN_ERR(svn_stream_contents_same2(&same, stream1, stream2, pool));
-
*changed_p = !same;
return SVN_NO_ERROR;
Modified: subversion/branches/inheritable-props/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_repos/deprecated.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_repos/deprecated.c Sat Oct 6 03:59:33 2012
@@ -254,6 +254,41 @@ svn_repos_begin_report(void **report_bat
}
svn_error_t *
+svn_repos_begin_report2(void **report_baton,
+ svn_revnum_t revnum,
+ svn_repos_t *repos,
+ const char *fs_base,
+ const char *target,
+ const char *tgt_path,
+ svn_boolean_t text_deltas,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ svn_boolean_t send_copyfrom_args,
+ const svn_delta_editor_t *editor,
+ void *edit_baton,
+ svn_repos_authz_func_t authz_read_func,
+ void *authz_read_baton,
+ apr_pool_t *pool)
+{
+ return svn_repos_begin_report3(report_baton,
+ revnum,
+ repos,
+ fs_base,
+ target,
+ tgt_path,
+ text_deltas,
+ depth,
+ ignore_ancestry,
+ send_copyfrom_args,
+ editor,
+ edit_baton,
+ authz_read_func,
+ authz_read_baton,
+ 0, /* disable zero-copy code path */
+ pool);
+}
+
+svn_error_t *
svn_repos_set_path2(void *baton, const char *path, svn_revnum_t rev,
svn_boolean_t start_empty, const char *lock_token,
apr_pool_t *pool)
Modified: subversion/branches/inheritable-props/subversion/libsvn_repos/fs-wrap.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_repos/fs-wrap.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_repos/fs-wrap.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_repos/fs-wrap.c Sat Oct 6 03:59:33 2012
@@ -50,6 +50,9 @@ svn_repos_fs_commit_txn(const char **con
{
svn_error_t *err, *err2;
const char *txn_name;
+ apr_hash_t *props;
+ apr_pool_t *iterpool;
+ apr_hash_index_t *hi;
*new_rev = SVN_INVALID_REVNUM;
@@ -57,6 +60,24 @@ svn_repos_fs_commit_txn(const char **con
SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
SVN_ERR(svn_repos__hooks_pre_commit(repos, txn_name, pool));
+ /* Remove any ephemeral transaction properties. */
+ SVN_ERR(svn_fs_txn_proplist(&props, txn, pool));
+ iterpool = svn_pool_create(pool);
+ for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
+ {
+ const void *key;
+ apr_hash_this(hi, &key, NULL, NULL);
+
+ svn_pool_clear(iterpool);
+
+ if (strncmp(key, SVN_PROP_TXN_PREFIX,
+ (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0)
+ {
+ SVN_ERR(svn_fs_change_txn_prop(txn, key, NULL, iterpool));
+ }
+ }
+ svn_pool_destroy(iterpool);
+
/* Commit. */
err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool);
if (! SVN_IS_VALID_REVNUM(*new_rev))
Modified: subversion/branches/inheritable-props/subversion/libsvn_repos/reporter.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_repos/reporter.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_repos/reporter.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_repos/reporter.c Sat Oct 6 03:59:33 2012
@@ -36,6 +36,7 @@
#include "private/svn_dep_compat.h"
#include "private/svn_fspath.h"
#include "private/svn_subr_private.h"
+#include "private/svn_string_private.h"
#define NUM_CACHED_SOURCE_ROOTS 4
@@ -102,13 +103,15 @@ typedef struct revision_info_t
driven by the client as it describes its working copy revisions. */
typedef struct report_baton_t
{
- /* Parameters remembered from svn_repos_begin_report2 */
+ /* Parameters remembered from svn_repos_begin_report3 */
svn_repos_t *repos;
const char *fs_base; /* fspath corresponding to wc anchor */
const char *s_operand; /* anchor-relative wc target (may be empty) */
svn_revnum_t t_rev; /* Revnum which the edit will bring the wc to */
const char *t_path; /* FS path the edit will bring the wc to */
svn_boolean_t text_deltas; /* Whether to report text deltas */
+ apr_size_t zero_copy_limit; /* Max item size that will be sent using
+ the zero-copy code path. */
/* If the client requested a specific depth, record it here; if the
client did not, then this is svn_depth_unknown, and the depth of
@@ -135,8 +138,10 @@ typedef struct report_baton_t
/* Cache for revision properties. This is used to eliminate redundant
revprop fetching. */
- apr_hash_t* revision_infos;
+ apr_hash_t *revision_infos;
+ /* This will not change. So, fetch it once and reuse it. */
+ svn_string_t *repos_uuid;
apr_pool_t *pool;
} report_baton_t;
@@ -513,25 +518,27 @@ delta_proplists(report_baton_t *b, svn_r
void *object, apr_pool_t *pool)
{
svn_fs_root_t *s_root;
- apr_hash_t *s_props, *t_props;
+ apr_hash_t *s_props = NULL, *t_props;
apr_array_header_t *prop_diffs;
int i;
svn_revnum_t crev;
- const char *uuid;
- svn_string_t *cr_str;
- revision_info_t* revision_info;
+ revision_info_t *revision_info;
svn_boolean_t changed;
const svn_prop_t *pc;
svn_lock_t *lock;
+ apr_hash_index_t *hi;
/* Fetch the created-rev and send entry props. */
SVN_ERR(svn_fs_node_created_rev(&crev, b->t_root, t_path, pool));
if (SVN_IS_VALID_REVNUM(crev))
{
+ /* convert committed-rev to string */
+ char buf[SVN_INT64_BUFFER_SIZE];
+ svn_string_t cr_str = { buf, svn__i64toa(buf, crev) };
+
/* Transmit the committed-rev. */
- cr_str = svn_string_createf(pool, "%ld", crev);
SVN_ERR(change_fn(b, object,
- SVN_PROP_ENTRY_COMMITTED_REV, cr_str, pool));
+ SVN_PROP_ENTRY_COMMITTED_REV, &cr_str, pool));
SVN_ERR(get_revision_info(b, crev, &revision_info, pool));
@@ -546,9 +553,8 @@ delta_proplists(report_baton_t *b, svn_r
revision_info->author, pool));
/* Transmit the UUID. */
- SVN_ERR(svn_fs_get_uuid(b->repos->fs, &uuid, pool));
SVN_ERR(change_fn(b, object, SVN_PROP_ENTRY_UUID,
- svn_string_create(uuid, pool), pool));
+ b->repos_uuid, pool));
}
/* Update lock properties. */
@@ -575,23 +581,83 @@ delta_proplists(report_baton_t *b, svn_r
/* If so, go ahead and get the source path's properties. */
SVN_ERR(svn_fs_node_proplist(&s_props, s_root, s_path, pool));
}
- else
- s_props = apr_hash_make(pool);
/* Get the target path's properties */
SVN_ERR(svn_fs_node_proplist(&t_props, b->t_root, t_path, pool));
- /* Now transmit the differences. */
- SVN_ERR(svn_prop_diffs(&prop_diffs, t_props, s_props, pool));
- for (i = 0; i < prop_diffs->nelts; i++)
+ if (s_props && apr_hash_count(s_props))
+ {
+ /* Now transmit the differences. */
+ SVN_ERR(svn_prop_diffs(&prop_diffs, t_props, s_props, pool));
+ for (i = 0; i < prop_diffs->nelts; i++)
+ {
+ pc = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t);
+ SVN_ERR(change_fn(b, object, pc->name, pc->value, pool));
+ }
+ }
+ else if (apr_hash_count(t_props))
+ {
+ /* So source, i.e. all new. Transmit all target props. */
+ for (hi = apr_hash_first(pool, t_props); hi; hi = apr_hash_next(hi))
+ {
+ const void *key;
+ void *val;
+
+ apr_hash_this(hi, &key, NULL, &val);
+ SVN_ERR(change_fn(b, object, key, val, pool));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Baton type to be passed into send_zero_copy_delta.
+ */
+typedef struct zero_copy_baton_t
+{
+ /* don't process data larger than this limit */
+ apr_size_t zero_copy_limit;
+
+ /* window handler and baton to send the data to */
+ svn_txdelta_window_handler_t dhandler;
+ void *dbaton;
+
+ /* return value: will be set to TRUE, if the data was processed. */
+ svn_boolean_t zero_copy_succeeded;
+} zero_copy_baton_t;
+
+/* Implement svn_fs_process_contents_func_t. If LEN is smaller than the
+ * limit given in *BATON, send the CONTENTS as an delta windows to the
+ * handler given in BATON and set the ZERO_COPY_SUCCEEDED flag in that
+ * BATON. Otherwise, reset it to FALSE.
+ * Use POOL for temporary allocations.
+ */
+static svn_error_t *
+send_zero_copy_delta(const unsigned char *contents,
+ apr_size_t len,
+ void *baton,
+ apr_pool_t *pool)
+{
+ zero_copy_baton_t *zero_copy_baton = baton;
+
+ /* if the item is too large, the caller must revert to traditional
+ streaming code. */
+ if (len > zero_copy_baton->zero_copy_limit)
{
- pc = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t);
- SVN_ERR(change_fn(b, object, pc->name, pc->value, pool));
+ zero_copy_baton->zero_copy_succeeded = FALSE;
+ return SVN_NO_ERROR;
}
+ SVN_ERR(svn_txdelta_send_contents(contents, len,
+ zero_copy_baton->dhandler,
+ zero_copy_baton->dbaton, pool));
+
+ /* all fine now */
+ zero_copy_baton->zero_copy_succeeded = TRUE;
return SVN_NO_ERROR;
}
+
/* Make the appropriate edits on FILE_BATON to change its contents and
properties from those in S_REV/S_PATH to those in B->t_root/T_PATH,
possibly using LOCK_TOKEN to determine if the client's lock on the file
@@ -645,6 +711,26 @@ delta_files(report_baton_t *b, void *fil
{
if (b->text_deltas)
{
+ /* if we send deltas against empty streams, we may use our
+ zero-copy code. */
+ if (b->zero_copy_limit > 0 && s_path == NULL)
+ {
+ zero_copy_baton_t baton = { b->zero_copy_limit
+ , dhandler
+ , dbaton
+ , FALSE};
+ svn_boolean_t called = FALSE;
+ SVN_ERR(svn_fs_try_process_file_contents(&called,
+ b->t_root, t_path,
+ send_zero_copy_delta,
+ &baton, pool));
+
+ /* data has been available and small enough,
+ i.e. been processed? */
+ if (called && baton.zero_copy_succeeded)
+ return SVN_NO_ERROR;
+ }
+
SVN_ERR(svn_fs_get_file_delta_stream(&dstream, s_root, s_path,
b->t_root, t_path, pool));
SVN_ERR(svn_txdelta_send_txstream(dstream, dhandler, dbaton, pool));
@@ -1463,7 +1549,7 @@ svn_repos_abort_report(void *baton, apr_
svn_error_t *
-svn_repos_begin_report2(void **report_baton,
+svn_repos_begin_report3(void **report_baton,
svn_revnum_t revnum,
svn_repos_t *repos,
const char *fs_base,
@@ -1477,14 +1563,18 @@ svn_repos_begin_report2(void **report_ba
void *edit_baton,
svn_repos_authz_func_t authz_read_func,
void *authz_read_baton,
+ apr_size_t zero_copy_limit,
apr_pool_t *pool)
{
report_baton_t *b;
+ const char *uuid;
if (depth == svn_depth_exclude)
return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL,
_("Request depth 'exclude' not supported"));
+ SVN_ERR(svn_fs_get_uuid(repos->fs, &uuid, pool));
+
/* Build a reporter baton. Copy strings in case the caller doesn't
keep track of them. */
b = apr_palloc(pool, sizeof(*b));
@@ -1495,6 +1585,7 @@ svn_repos_begin_report2(void **report_ba
b->t_path = switch_path ? svn_fspath__canonicalize(switch_path, pool)
: svn_fspath__join(b->fs_base, s_operand, pool);
b->text_deltas = text_deltas;
+ b->zero_copy_limit = zero_copy_limit;
b->requested_depth = depth;
b->ignore_ancestry = ignore_ancestry;
b->send_copyfrom_args = send_copyfrom_args;
@@ -1508,6 +1599,7 @@ svn_repos_begin_report2(void **report_ba
b->reader = svn_spillbuf__reader_create(1000 /* blocksize */,
1000000 /* maxsize */,
pool);
+ b->repos_uuid = svn_string_create(uuid, pool);
/* Hand reporter back to client. */
*report_baton = b;
Modified: subversion/branches/inheritable-props/subversion/libsvn_subr/cache-membuffer.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_subr/cache-membuffer.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_subr/cache-membuffer.c Sat Oct 6 03:59:33 2012
@@ -33,11 +33,12 @@
#include "svn_string.h"
#include "private/svn_dep_compat.h"
#include "private/svn_mutex.h"
+#include "private/svn_pseudo_md5.h"
/*
* This svn_cache__t implementation actually consists of two parts:
* a shared (per-process) singleton membuffer cache instance and shallow
- * svn_cache__t frontend instances that each use different key spaces.
+ * svn_cache__t front-end instances that each use different key spaces.
* For data management, they all forward to the singleton membuffer cache.
*
* A membuffer cache consists of two parts:
@@ -65,7 +66,7 @@
*
* Insertion can occur at only one, sliding position. It is marked by its
* offset in the data buffer plus the index of the first used entry at or
- * behind that position. If this gap is too small to accomodate the new
+ * behind that position. If this gap is too small to accommodate the new
* item, the insertion window is extended as described below. The new entry
* will always be inserted at the bottom end of the window and since the
* next used entry is known, properly sorted insertion is possible.
@@ -79,7 +80,7 @@
* get evicted, it is moved to the begin of that window and the window is
* moved.
*
- * Moreover, the entry's hits get halfed to make that entry more likely to
+ * Moreover, the entry's hits get halved to make that entry more likely to
* be removed the next time the sliding insertion / removal window comes by.
* As a result, frequently used entries are likely not to be dropped until
* they get not used for a while. Also, even a cache thrashing situation
@@ -100,22 +101,34 @@
* on their hash key.
*/
-/* A 8-way associative cache seems to be a good compromise between
+/* A 16-way associative cache seems to be a good compromise between
* performance (worst-case lookups) and efficiency-loss due to collisions.
*
* This value may be changed to any positive integer.
*/
-#define GROUP_SIZE 8
+#define GROUP_SIZE 16
/* For more efficient copy operations, let'a align all data items properly.
* Must be a power of 2.
*/
#define ITEM_ALIGNMENT 16
-/* Don't create cache segments smaller than this value unless the total
- * cache size itself is smaller.
+/* By default, don't create cache segments smaller than this value unless
+ * the total cache size itself is smaller.
*/
-#define MIN_SEGMENT_SIZE 0x2000000ull
+#define DEFAULT_MIN_SEGMENT_SIZE 0x2000000ull
+
+/* The minimum segment size we will allow for multi-segmented caches
+ */
+#define MIN_SEGMENT_SIZE 0x10000ull
+
+/* The maximum number of segments allowed. Larger numbers reduce the size
+ * of each segment, in turn reducing the max size of a cachable item.
+ * Also, each segment gets its own lock object. The actual number supported
+ * by the OS may therefore be lower and svn_cache__membuffer_cache_create
+ * may return an error.
+ */
+#define MAX_SEGMENT_COUNT 0x10000
/* We don't mark the initialization status for every group but initialize
* a number of groups at once. That will allow for a very small init flags
@@ -130,10 +143,6 @@
*/
#define NO_INDEX APR_UINT32_MAX
-/* Invalid buffer offset reference value. Equivalent to APR_UINT32_T(-1)
- */
-#define NO_OFFSET APR_UINT64_MAX
-
/* To save space in our group structure, we only use 32 bit size values
* and, therefore, limit the size of each entry to just below 4GB.
* Supporting larger items is not a good idea as the data transfer
@@ -298,8 +307,7 @@ typedef apr_uint64_t entry_key_t[2];
/* A single dictionary entry. Since all entries will be allocated once
* during cache creation, those entries might be either used or unused.
* An entry is used if and only if it is contained in the doubly-linked
- * list of used entries. An entry is unused if and only if its OFFSET
- * member is NO_OFFSET.
+ * list of used entries.
*/
typedef struct entry_t
{
@@ -307,8 +315,7 @@ typedef struct entry_t
*/
entry_key_t key;
- /* If NO_OFFSET, the entry is not in used. Otherwise, it is the offset
- * of the cached item's serialized data within the data buffer.
+ /* The offset of the cached item's serialized data within the data buffer.
*/
apr_uint64_t offset;
@@ -346,7 +353,14 @@ typedef struct entry_t
/* We group dictionary entries to make this GROUP-SIZE-way assicative.
*/
-typedef entry_t entry_group_t[GROUP_SIZE];
+typedef struct entry_group_t
+{
+ /* number of entries used [0 .. USED-1] */
+ apr_size_t used;
+
+ /* the actual entries */
+ entry_t entries[GROUP_SIZE];
+} entry_group_t;
/* The cache header structure.
*/
@@ -446,6 +460,12 @@ struct svn_membuffer_t
* thread-safe.
*/
apr_thread_rwlock_t *lock;
+
+ /* If set, write access will wait until they get exclusive access.
+ * Otherwise, they will become no-ops if the segment is currently
+ * read-locked.
+ */
+ svn_boolean_t allow_blocking_writes;
#endif
};
@@ -473,18 +493,48 @@ read_lock_cache(svn_membuffer_t *cache)
return SVN_NO_ERROR;
}
-/* If locking is supported for CACHE, aquire a write lock for it.
+/* If locking is supported for CACHE, acquire a write lock for it.
*/
static svn_error_t *
-write_lock_cache(svn_membuffer_t *cache)
+write_lock_cache(svn_membuffer_t *cache, svn_boolean_t *success)
{
#if APR_HAS_THREADS
if (cache->lock)
- {
- apr_status_t status = apr_thread_rwlock_wrlock(cache->lock);
- if (status)
- return svn_error_wrap_apr(status, _("Can't write-lock cache mutex"));
- }
+ {
+ apr_status_t status;
+ if (cache->allow_blocking_writes)
+ {
+ status = apr_thread_rwlock_wrlock(cache->lock);
+ }
+ else
+ {
+ status = apr_thread_rwlock_trywrlock(cache->lock);
+ if (SVN_LOCK_IS_BUSY(status))
+ {
+ *success = FALSE;
+ status = APR_SUCCESS;
+ }
+ }
+
+ if (status)
+ return svn_error_wrap_apr(status,
+ _("Can't write-lock cache mutex"));
+ }
+#endif
+ return SVN_NO_ERROR;
+}
+
+/* If locking is supported for CACHE, acquire an unconditional write lock
+ * for it.
+ */
+static svn_error_t *
+force_write_lock_cache(svn_membuffer_t *cache)
+{
+#if APR_HAS_THREADS
+ apr_status_t status = apr_thread_rwlock_wrlock(cache->lock);
+ if (status)
+ return svn_error_wrap_apr(status,
+ _("Can't write-lock cache mutex"));
#endif
return SVN_NO_ERROR;
}
@@ -510,7 +560,7 @@ unlock_cache(svn_membuffer_t *cache, svn
}
/* If supported, guard the execution of EXPR with a read lock to cache.
- * Macro has been modelled after SVN_MUTEX__WITH_LOCK.
+ * Macro has been modeled after SVN_MUTEX__WITH_LOCK.
*/
#define WITH_READ_LOCK(cache, expr) \
do { \
@@ -519,12 +569,29 @@ do {
} while (0)
/* If supported, guard the execution of EXPR with a write lock to cache.
- * Macro has been modelled after SVN_MUTEX__WITH_LOCK.
- */
-#define WITH_WRITE_LOCK(cache, expr) \
-do { \
- SVN_ERR(write_lock_cache(cache)); \
- SVN_ERR(unlock_cache(cache, (expr))); \
+ * Macro has been modeled after SVN_MUTEX__WITH_LOCK.
+ *
+ * The write lock process is complicated if we don't allow to wait for
+ * the lock: If we didn't get the lock, we may still need to remove an
+ * existing entry for the given key because that content is now stale.
+ * Once we discovered such an entry, we unconditionally do a blocking
+ * wait for the write lock. In case no old content could be found, a
+ * failing lock attempt is simply a no-op and we exit the macro.
+ */
+#define WITH_WRITE_LOCK(cache, expr) \
+do { \
+ svn_boolean_t got_lock = TRUE; \
+ SVN_ERR(write_lock_cache(cache, &got_lock)); \
+ if (!got_lock) \
+ { \
+ svn_boolean_t exists; \
+ SVN_ERR(entry_exists(cache, group_index, key, &exists)); \
+ if (exists) \
+ SVN_ERR(force_write_lock_cache(cache)); \
+ else \
+ break; \
+ } \
+ SVN_ERR(unlock_cache(cache, (expr))); \
} while (0)
/* Resolve a dictionary entry reference, i.e. return the entry
@@ -533,7 +600,7 @@ do {
static APR_INLINE entry_t *
get_entry(svn_membuffer_t *cache, apr_uint32_t idx)
{
- return &cache->directory[idx / GROUP_SIZE][idx % GROUP_SIZE];
+ return &cache->directory[idx / GROUP_SIZE].entries[idx % GROUP_SIZE];
}
/* Get the entry references for the given ENTRY.
@@ -541,7 +608,11 @@ get_entry(svn_membuffer_t *cache, apr_ui
static APR_INLINE apr_uint32_t
get_index(svn_membuffer_t *cache, entry_t *entry)
{
- return (apr_uint32_t)(entry - (entry_t *)cache->directory);
+ apr_uint32_t group_index
+ = ((char *)entry - (char *)cache->directory) / sizeof(entry_group_t);
+
+ return group_index * GROUP_SIZE
+ + (apr_uint32_t)(entry - cache->directory[group_index].entries);
}
/* Remove the used ENTRY from the CACHE, i.e. make it "unused".
@@ -550,11 +621,16 @@ get_index(svn_membuffer_t *cache, entry_
static void
drop_entry(svn_membuffer_t *cache, entry_t *entry)
{
+ /* the group that ENTRY belongs to plus a number of useful index values
+ */
apr_uint32_t idx = get_index(cache, entry);
+ apr_uint32_t group_index = idx / GROUP_SIZE;
+ entry_group_t *group = &cache->directory[group_index];
+ apr_uint32_t last_in_group = group_index * GROUP_SIZE + group->used - 1;
/* Only valid to be called for used entries.
*/
- assert(entry->offset != NO_OFFSET);
+ assert(idx <= last_in_group);
/* update global cache usage counters
*/
@@ -597,9 +673,35 @@ drop_entry(svn_membuffer_t *cache, entry
else
get_entry(cache, entry->next)->previous = entry->previous;
- /* Mark the entry as unused.
+ /* Move last entry into hole (if the removed one is not the last used).
+ * We need to do this since all used entries are at the beginning of
+ * the group's entries array.
*/
- entry->offset = NO_OFFSET;
+ if (idx < last_in_group)
+ {
+ /* copy the last used entry to the removed entry's index
+ */
+ *entry = group->entries[group->used-1];
+
+ /* update foreign links to new index
+ */
+ if (last_in_group == cache->next)
+ cache->next = idx;
+
+ if (entry->previous == NO_INDEX)
+ cache->first = idx;
+ else
+ get_entry(cache, entry->previous)->next = idx;
+
+ if (entry->next == NO_INDEX)
+ cache->last = idx;
+ else
+ get_entry(cache, entry->next)->previous = idx;
+ }
+
+ /* Update the number of used entries.
+ */
+ group->used--;
}
/* Insert ENTRY into the chain of used dictionary entries. The entry's
@@ -609,21 +711,28 @@ drop_entry(svn_membuffer_t *cache, entry
static void
insert_entry(svn_membuffer_t *cache, entry_t *entry)
{
+ /* the group that ENTRY belongs to plus a number of useful index values
+ */
apr_uint32_t idx = get_index(cache, entry);
+ apr_uint32_t group_index = idx / GROUP_SIZE;
+ entry_group_t *group = &cache->directory[group_index];
entry_t *next = cache->next == NO_INDEX
? NULL
: get_entry(cache, cache->next);
/* The entry must start at the beginning of the insertion window.
+ * It must also be the first unused entry in the group.
*/
assert(entry->offset == cache->current_data);
+ assert(idx == group_index * GROUP_SIZE + group->used);
cache->current_data = ALIGN_VALUE(entry->offset + entry->size);
- /* update global cache usage counters
+ /* update usage counters
*/
cache->used_entries++;
cache->data_used += entry->size;
entry->hit_count = 0;
+ group->used++;
/* update entry chain
*/
@@ -672,9 +781,11 @@ static apr_uint32_t
get_group_index(svn_membuffer_t **cache,
entry_key_t key)
{
- /* select the cache segment to use */
- *cache = &(*cache)[key[0] & ((*cache)->segment_count -1)];
- return key[0] % (*cache)->group_count;
+ svn_membuffer_t *segment0 = *cache;
+
+ /* select the cache segment to use. they have all the same group_count */
+ *cache = &segment0[key[0] & (segment0->segment_count -1)];
+ return key[1] % segment0->group_count;
}
/* Reduce the hit count of ENTRY and update the accumunated hit info
@@ -710,7 +821,7 @@ static void
initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
{
unsigned char bit_mask;
- apr_uint32_t i, j;
+ apr_uint32_t i;
/* range of groups to initialize due to GROUP_INIT_GRANULARITY */
apr_uint32_t first_index =
@@ -720,8 +831,7 @@ initialize_group(svn_membuffer_t *cache,
last_index = cache->group_count;
for (i = first_index; i < last_index; ++i)
- for (j = 0; j < GROUP_SIZE; j++)
- cache->directory[i][j].offset = NO_OFFSET;
+ cache->directory[i].used = 0;
/* set the "initialized" bit for these groups */
bit_mask
@@ -748,13 +858,13 @@ find_entry(svn_membuffer_t *cache,
const apr_uint64_t to_find[2],
svn_boolean_t find_empty)
{
- entry_t *group;
+ entry_group_t *group;
entry_t *entry = NULL;
int i;
/* get the group that *must* contain the entry
*/
- group = &cache->directory[group_index][0];
+ group = &cache->directory[group_index];
/* If the entry group has not been initialized, yet, there is no data.
*/
@@ -763,7 +873,7 @@ find_entry(svn_membuffer_t *cache,
if (find_empty)
{
initialize_group(cache, group_index);
- entry = group;
+ entry = &group->entries[0];
/* initialize entry for the new key */
entry->key[0] = to_find[0];
@@ -775,54 +885,51 @@ find_entry(svn_membuffer_t *cache,
/* try to find the matching entry
*/
- for (i = 0; i < GROUP_SIZE; ++i)
- if ( group[i].offset != NO_OFFSET
- && to_find[0] == group[i].key[0]
- && to_find[1] == group[i].key[1])
+ for (i = 0; i < group->used; ++i)
+ if ( to_find[0] == group->entries[i].key[0]
+ && to_find[1] == group->entries[i].key[1])
{
/* found it
*/
- entry = &group[i];
+ entry = &group->entries[i];
if (find_empty)
drop_entry(cache, entry);
-
- return entry;
+ else
+ return entry;
}
/* None found. Are we looking for a free entry?
*/
if (find_empty)
{
- /* look for an empty entry and return that ...
+ /* if there is no empty entry, delete the oldest entry
*/
- for (i = 0; i < GROUP_SIZE; ++i)
- if (group[i].offset == NO_OFFSET)
- {
- entry = &group[i];
- break;
- }
-
- /* ... or, if none is empty, delete the oldest entry
- */
- if (entry == NULL)
+ if (group->used == GROUP_SIZE)
{
- entry = &group[0];
+ /* every entry gets the same chance of being removed.
+ * Otherwise, we free the first entry, fill it and
+ * remove it again on the next occasion without considering
+ * the other entries in this group.
+ */
+ entry = &group->entries[rand() % GROUP_SIZE];
for (i = 1; i < GROUP_SIZE; ++i)
- if (entry->hit_count > group[i].hit_count)
- entry = &group[i];
+ if (entry->hit_count > group->entries[i].hit_count)
+ entry = &group->entries[i];
/* for the entries that don't have been removed,
* reduce their hitcounts to put them at a relative
* disadvantage the next time.
*/
for (i = 0; i < GROUP_SIZE; ++i)
- if (entry != &group[i])
+ if (entry != &group->entries[i])
let_entry_age(cache, entry);
drop_entry(cache, entry);
}
- /* initialize entry for the new key */
+ /* initialize entry for the new key
+ */
+ entry = &group->entries[group->used];
entry->key[0] = to_find[0];
entry->key[1] = to_find[1];
}
@@ -950,22 +1057,38 @@ ensure_data_insertable(svn_membuffer_t *
}
else
{
- /* Roll the dice and determine a threshold somewhere from 0 up
- * to 2 times the average hit count.
- */
- average_hit_value = cache->hit_count / cache->used_entries;
- threshold = (average_hit_value+1) * (rand() % 4096) / 2048;
+ svn_boolean_t keep;
- /* Drop the entry from the end of the insertion window, if it
- * has been hit less than the threshold. Otherwise, keep it and
- * move the insertion window one entry further.
- */
- if (entry->hit_count >= threshold)
+ if (cache->hit_count > cache->used_entries)
+ {
+ /* Roll the dice and determine a threshold somewhere from 0 up
+ * to 2 times the average hit count.
+ */
+ average_hit_value = cache->hit_count / cache->used_entries;
+ threshold = (average_hit_value+1) * (rand() % 4096) / 2048;
+
+ keep = entry->hit_count >= threshold;
+ }
+ else
+ {
+ /* general hit count is low. Keep everything that got hit
+ * at all and assign some 50% survival chance to everything
+ * else.
+ */
+ keep = (entry->hit_count > 0) || (rand() & 1);
+ }
+
+ /* keepers or destroyers? */
+ if (keep)
{
move_entry(cache, entry);
}
else
{
+ /* Drop the entry from the end of the insertion window, if it
+ * has been hit less than the threshold. Otherwise, keep it and
+ * move the insertion window one entry further.
+ */
drop_size += entry->size;
drop_entry(cache, entry);
}
@@ -999,56 +1122,67 @@ static void* secure_aligned_alloc(apr_po
return memory;
}
-/* Create a new membuffer cache instance. If the TOTAL_SIZE of the
- * memory i too small to accomodate the DICTIONARY_SIZE, the latte
- * will be resized automatically. Also, a minumum size is assured
- * for the DICTIONARY_SIZE. THREAD_SAFE may be FALSE, if there will
- * be no concurrent acccess to the CACHE returned.
- *
- * All allocations, in particular the data buffer and dictionary will
- * be made from POOL.
- */
svn_error_t *
svn_cache__membuffer_cache_create(svn_membuffer_t **cache,
apr_size_t total_size,
apr_size_t directory_size,
+ apr_size_t segment_count,
svn_boolean_t thread_safe,
+ svn_boolean_t allow_blocking_writes,
apr_pool_t *pool)
{
svn_membuffer_t *c;
- apr_uint32_t segment_count_shift = 0;
- apr_uint32_t segment_count = 1;
-
apr_uint32_t seg;
apr_uint32_t group_count;
apr_uint32_t group_init_size;
apr_uint64_t data_size;
apr_uint64_t max_entry_size;
- /* Determine a reasonable number of cache segments. Segmentation is
- * only useful for multi-threaded / multi-core servers as it reduces
- * lock contention on these systems.
- *
- * But on these systems, we can assume that ample memory has been
- * allocated to this cache. Smaller caches should not be segmented
- * as this severely limites the maximum size of cachable items.
- *
- * Segments should not be smaller than 32MB and max. cachable item
- * size should grow as fast as segmentation.
+ /* Limit the segment count
*/
- while (((2 * MIN_SEGMENT_SIZE) << (2 * segment_count_shift)) < total_size)
- ++segment_count_shift;
+ if (segment_count > MAX_SEGMENT_COUNT)
+ segment_count = MAX_SEGMENT_COUNT;
+ if (segment_count * MIN_SEGMENT_SIZE > total_size)
+ segment_count = total_size / MIN_SEGMENT_SIZE;
+
+ /* The segment count must be a power of two. Round it down as necessary.
+ */
+ while ((segment_count & (segment_count-1)) != 0)
+ segment_count &= segment_count-1;
+
+ /* if the caller hasn't provided a reasonable segment count or the above
+ * limitations set it to 0, derive one from the absolute cache size
+ */
+ if (segment_count < 1)
+ {
+ /* Determine a reasonable number of cache segments. Segmentation is
+ * only useful for multi-threaded / multi-core servers as it reduces
+ * lock contention on these systems.
+ *
+ * But on these systems, we can assume that ample memory has been
+ * allocated to this cache. Smaller caches should not be segmented
+ * as this severely limits the maximum size of cachable items.
+ *
+ * Segments should not be smaller than 32MB and max. cachable item
+ * size should grow as fast as segmentation.
+ */
- segment_count = 1 << segment_count_shift;
+ apr_uint32_t segment_count_shift = 0;
+ while (((2 * DEFAULT_MIN_SEGMENT_SIZE) << (2 * segment_count_shift))
+ < total_size)
+ ++segment_count_shift;
+
+ segment_count = 1 << segment_count_shift;
+ }
/* allocate cache as an array of segments / cache objects */
c = apr_palloc(pool, segment_count * sizeof(*c));
/* Split total cache size into segments of equal size
*/
- total_size >>= segment_count_shift;
- directory_size >>= segment_count_shift;
+ total_size /= segment_count;
+ directory_size /= segment_count;
/* prevent pathological conditions: ensure a certain minimum cache size
*/
@@ -1143,6 +1277,10 @@ svn_cache__membuffer_cache_create(svn_me
if (status)
return svn_error_wrap_apr(status, _("Can't create cache mutex"));
}
+
+ /* Select the behavior of write operations.
+ */
+ c[seg].allow_blocking_writes = allow_blocking_writes;
#endif
}
@@ -1152,6 +1290,40 @@ svn_cache__membuffer_cache_create(svn_me
return SVN_NO_ERROR;
}
+/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
+ * by the hash value TO_FIND and set *FOUND accordingly.
+ *
+ * Note: This function requires the caller to serialize access.
+ * Don't call it directly, call entry_exists instead.
+ */
+static svn_error_t *
+entry_exists_internal(svn_membuffer_t *cache,
+ apr_uint32_t group_index,
+ entry_key_t to_find,
+ svn_boolean_t *found)
+{
+ *found = find_entry(cache, group_index, to_find, FALSE) != NULL;
+ return SVN_NO_ERROR;
+}
+
+/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
+ * by the hash value TO_FIND and set *FOUND accordingly.
+ */
+static svn_error_t *
+entry_exists(svn_membuffer_t *cache,
+ apr_uint32_t group_index,
+ entry_key_t to_find,
+ svn_boolean_t *found)
+{
+ WITH_READ_LOCK(cache,
+ entry_exists_internal(cache,
+ group_index,
+ to_find,
+ found));
+
+ return SVN_NO_ERROR;
+}
+
/* Try to insert the serialized item given in BUFFER with SIZE into
* the group GROUP_INDEX of CACHE and uniquely identify it by hash
@@ -1174,15 +1346,38 @@ membuffer_cache_set_internal(svn_membuff
DEBUG_CACHE_MEMBUFFER_TAG_ARG
apr_pool_t *scratch_pool)
{
+ /* first, look for a previous entry for the given key */
+ entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
+
+ /* if there is an old version of that entry and the new data fits into
+ * the old spot, just re-use that space. */
+ if (buffer && entry && entry->size >= size)
+ {
+ cache->data_used += size - entry->size;
+ entry->size = size;
+
+#ifdef SVN_DEBUG_CACHE_MEMBUFFER
+
+ /* Remember original content, type and key (hashes)
+ */
+ SVN_ERR(store_content_part(tag, buffer, size, scratch_pool));
+ memcpy(&entry->tag, tag, sizeof(*tag));
+
+#endif
+
+ if (size)
+ memcpy(cache->data + entry->offset, buffer, size);
+
+ cache->total_writes++;
+ return SVN_NO_ERROR;
+ }
+
/* if necessary, enlarge the insertion window.
*/
if ( buffer != NULL
&& cache->max_entry_size >= size
&& ensure_data_insertable(cache, size))
{
- /* first, look for a previous entry for the given key */
- entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
-
#ifdef SVN_DEBUG_CACHE_MEMBUFFER
/* Remember original content, type and key (hashes)
@@ -1192,26 +1387,17 @@ membuffer_cache_set_internal(svn_membuff
#endif
- /* if there is an old version of that entry and the new data fits into
- * the old spot, just re-use that space. */
- if (entry && entry->size >= size)
- {
- entry->size = size;
- }
- else
- {
- /* Remove old data for this key, if that exists.
- * Get an unused entry for the key and and initialize it with
- * the serialized item's (future) position within data buffer.
- */
- entry = find_entry(cache, group_index, to_find, TRUE);
- entry->size = size;
- entry->offset = cache->current_data;
+ /* Remove old data for this key, if that exists.
+ * Get an unused entry for the key and and initialize it with
+ * the serialized item's (future) position within data buffer.
+ */
+ entry = find_entry(cache, group_index, to_find, TRUE);
+ entry->size = size;
+ entry->offset = cache->current_data;
- /* Link the entry properly.
- */
- insert_entry(cache, entry);
- }
+ /* Link the entry properly.
+ */
+ insert_entry(cache, entry);
/* Copy the serialized item data into the cache.
*/
@@ -1224,8 +1410,10 @@ membuffer_cache_set_internal(svn_membuff
{
/* if there is already an entry for this key, drop it.
*/
- find_entry(cache, group_index, to_find, TRUE);
+ if (entry)
+ drop_entry(cache, entry);
}
+
return SVN_NO_ERROR;
}
@@ -1523,7 +1711,7 @@ membuffer_cache_set_partial_internal(svn
#endif
- /* modify it, preferrably in-situ.
+ /* modify it, preferably in-situ.
*/
err = func((void **)&data, &size, baton, scratch_pool);
@@ -1544,7 +1732,7 @@ membuffer_cache_set_partial_internal(svn
{
/* Remove the old entry and try to make space for the new one.
*/
- drop_entry(cache, entry);
+ entry = find_entry(cache, group_index, to_find, TRUE);
if ( (cache->max_entry_size >= size)
&& ensure_data_insertable(cache, size))
{
@@ -1692,7 +1880,31 @@ combine_key(svn_membuffer_cache_t *cache
if (key_len == APR_HASH_KEY_STRING)
key_len = strlen((const char *) key);
- apr_md5((unsigned char*)cache->combined_key, key, key_len);
+ if (key_len < 16)
+ {
+ apr_uint32_t data[4] = { 0 };
+ memcpy(data, key, key_len);
+
+ svn__pseudo_md5_15((apr_uint32_t *)cache->combined_key, data);
+ }
+ else if (key_len < 32)
+ {
+ apr_uint32_t data[8] = { 0 };
+ memcpy(data, key, key_len);
+
+ svn__pseudo_md5_31((apr_uint32_t *)cache->combined_key, data);
+ }
+ else if (key_len < 64)
+ {
+ apr_uint32_t data[16] = { 0 };
+ memcpy(data, key, key_len);
+
+ svn__pseudo_md5_63((apr_uint32_t *)cache->combined_key, data);
+ }
+ else
+ {
+ apr_md5((unsigned char*)cache->combined_key, key, key_len);
+ }
cache->combined_key[0] ^= cache->prefix[0];
cache->combined_key[1] ^= cache->prefix[1];
Modified: subversion/branches/inheritable-props/subversion/libsvn_subr/cache-memcache.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_subr/cache-memcache.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_subr/cache-memcache.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_subr/cache-memcache.c Sat Oct 6 03:59:33 2012
@@ -470,7 +470,8 @@ add_memcache_server(const char *name,
0, /* min connections */
5, /* soft max connections */
10, /* hard max connections */
- 50, /* connection time to live (secs) */
+ /* time to live (in microseconds) */
+ apr_time_from_sec(50),
&server);
if (apr_err != APR_SUCCESS)
{
Modified: subversion/branches/inheritable-props/subversion/libsvn_subr/cache_config.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_subr/cache_config.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_subr/cache_config.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_subr/cache_config.c Sat Oct 6 03:59:33 2012
@@ -119,7 +119,9 @@ svn_cache__get_global_membuffer_cache(vo
&new_cache,
(apr_size_t)cache_size,
(apr_size_t)(cache_size / 10),
+ 0,
! svn_cache_config_get()->single_threaded,
+ FALSE,
pool);
/* Some error occured. Most likely it's an OOM error but we don't
Modified: subversion/branches/inheritable-props/subversion/libsvn_subr/ssl_client_cert_pw_providers.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_subr/ssl_client_cert_pw_providers.c?rev=1394915&r1=1394914&r2=1394915&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_subr/ssl_client_cert_pw_providers.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_subr/ssl_client_cert_pw_providers.c Sat Oct 6 03:59:33 2012
@@ -82,7 +82,7 @@ svn_auth__ssl_client_cert_pw_get(svn_boo
return SVN_NO_ERROR;
}
*done = FALSE;
- return FALSE;
+ return SVN_NO_ERROR;
}
/* This implements the svn_auth__password_set_t interface.