Add threadpool support, add optional timings compile flag

This commit is contained in:
Dave Davenport 2015-11-14 13:47:45 +01:00
parent 45c8de9387
commit 80e873c8a3
7 changed files with 188 additions and 72 deletions

View file

@ -34,6 +34,7 @@ rofi_SOURCES=\
config/config.c\
source/helper.c\
source/textbox.c\
source/timings.c\
source/history.c\
source/scrollbar.c\
source/i3-support.c\
@ -49,6 +50,7 @@ rofi_SOURCES=\
include/rofi.h\
include/keyb.h\
include/helper.h\
include/timings.h\
include/history.h\
include/textbox.h\
include/scrollbar.h\

View file

@ -3,15 +3,15 @@ AC_INIT([rofi], [0.15.12-devel], [https://github.com/DaveDavenport/rofi/])
AC_CONFIG_SRCDIR([source/rofi.c])
AC_CONFIG_HEADER([config.h])
##
# Setup automake to be silent and in foreign mode.
##
dnl ---------------------------------------------------------------------
dnl Setup automake to be silent and in foreign mode.
dnl ---------------------------------------------------------------------
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects dist-xz])
AM_SILENT_RULES([yes])
##
# Check for compiler
##
dnl ---------------------------------------------------------------------
dnl Check for compiler
dnl ---------------------------------------------------------------------
AC_PROG_CC([clang gcc cc])
AC_PROG_CC_C99
AM_PROG_CC_C_O
@ -21,27 +21,31 @@ AC_USE_SYSTEM_EXTENSIONS
dnl ---------------------------------------------------------------------
dnl Disable window mode
dnl ---------------------------------------------------------------------
AC_ARG_ENABLE([windowmode], AC_HELP_STRING([--disable-windowmode],[Disable window mode]))
AS_IF([ test "x$enable_windowmode" != "xno"], [AC_DEFINE([WINDOW_MODE],[1],[Enable the window mode])])
##
# I3 check
##
dnl ---------------------------------------------------------------------
dnl Output timing information
dnl ---------------------------------------------------------------------
AC_ARG_ENABLE([timings], AC_HELP_STRING([--enable-timings],[Enable timing output]))
AS_IF( [ test "x$enable_timings" = "xyes"], [AC_DEFINE([TIMINGS],[1], [Enable timings output])])
dnl ---------------------------------------------------------------------
dnl I3 check
dnl ---------------------------------------------------------------------
AC_ARG_ENABLE(i3support, [AS_HELP_STRING([--disable-i3support], [Disable check for i3 support])])
AS_IF([test "x$enable_i3support" != xno && test "x$enable_windowmode" != "xno"],[AC_CHECK_HEADERS([i3/ipc.h], [i3_header=yes; break;])])
##
# Check for C functions.
##
dnl ---------------------------------------------------------------------
dnl Check for C functions.
dnl ---------------------------------------------------------------------
AC_CHECK_FUNC([getdelim],, AC_MSG_ERROR("Could not find getdelim in c library"))
AC_CHECK_FUNC([sysconf],, AC_MSG_ERROR("Could not find sysconf"))
##
# Check dependencies
##
dnl ---------------------------------------------------------------------
dnl Check dependencies
dnl ---------------------------------------------------------------------
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([x11], [x11])
@ -66,6 +70,11 @@ echo "I3 support: Enabled"
else
echo "I3 support: Disabled"
fi
if test x$enable_timings = xyes; then
echo "Timing output: Enabled"
else
echo "Timing output: Disabled"
fi
echo "-------------------------------------"
echo "Now type 'make' to build"
echo ""

View file

@ -7,6 +7,7 @@
#include <textbox.h>
#include <cairo.h>
#include <cairo-xlib.h>
#include "timings.h"
#include "keyb.h"
/**

View file

@ -33,14 +33,14 @@ typedef struct
typedef enum
{
TB_AUTOHEIGHT = 1 << 0,
TB_AUTOWIDTH = 1 << 1,
TB_LEFT = 1 << 16,
TB_RIGHT = 1 << 17,
TB_CENTER = 1 << 18,
TB_EDITABLE = 1 << 19,
TB_MARKUP = 1 << 20,
TB_WRAP = 1 << 21,
TB_AUTOHEIGHT = 1 << 0,
TB_AUTOWIDTH = 1 << 1,
TB_LEFT = 1 << 16,
TB_RIGHT = 1 << 17,
TB_CENTER = 1 << 18,
TB_EDITABLE = 1 << 19,
TB_MARKUP = 1 << 20,
TB_WRAP = 1 << 21,
} TextboxFlags;
typedef enum

View file

@ -37,6 +37,9 @@
#include "dialogs/dmenu.h"
#include "helper.h"
// We limit at 1000000 rows for now.
#define DMENU_MAX_ROWS 1000000
struct range_pair
{
unsigned int start;
@ -59,7 +62,8 @@ typedef struct _DmenuModePrivateData
static char **get_dmenu ( unsigned int *length )
{
char **retv = NULL;
TICK_N ( "Read stdin START" )
char **retv = NULL;
unsigned int rvlength = 1;
*length = 0;
@ -75,18 +79,19 @@ static char **get_dmenu ( unsigned int *length )
data[l - 1] = '\0';
}
retv[( *length )] = data; //copy;
retv[( *length ) + 1] = NULL;
data = NULL;
data_l = 0;
retv[( *length )] = data; //copy;
data = NULL;
data_l = 0;
( *length )++;
// Stop when we hit 2³¹ entries.
if ( ( *length ) == INT_MAX ) {
return retv;
if ( ( *length ) >= DMENU_MAX_ROWS ) {
break;
}
}
retv = g_realloc ( retv, ( *length + 1 ) * sizeof ( char* ) );
retv[( *length ) + 1] = NULL;
retv = g_realloc ( retv, ( *length + 1 ) * sizeof ( char* ) );
TICK_N ( "Read stdin STOP" )
return retv;
}

View file

@ -517,7 +517,7 @@ void config_sanity_check ( Display *display )
config.threads = 1;
long procs = sysconf ( _SC_NPROCESSORS_CONF );
if ( procs > 0 ) {
config.threads = MIN(procs,UINT_MAX);
config.threads = MIN ( procs, UINT_MAX );
}
}
// If alternative row is not set, copy the normal background color.

View file

@ -23,8 +23,8 @@
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
@ -94,6 +94,8 @@ cairo_t *draw = NULL;
XIM xim;
XIC xic;
GThreadPool *tpool = NULL;
/**
* @param name Name of the switcher to lookup.
*
@ -705,18 +707,21 @@ static void menu_mouse_navigation ( MenuState *state, XButtonEvent *xbe )
}
}
typedef struct
typedef struct _thread_state
{
MenuState *state;
char **tokens;
unsigned int start;
unsigned int stop;
unsigned int count;
GCond *cond;
GMutex *mutex;
unsigned int *acount;
void ( *callback )( struct _thread_state *t, gpointer data );
}thread_state;
static gpointer filter_elements ( gpointer data )
static void filter_elements ( thread_state *t, G_GNUC_UNUSED gpointer user_data )
{
thread_state *t = (thread_state *) data;
// input changed
for ( unsigned int i = t->start; i < t->stop; i++ ) {
int match = t->state->sw->token_match ( t->tokens,
@ -735,39 +740,76 @@ static gpointer filter_elements ( gpointer data )
t->count++;
}
}
return 0;
g_mutex_lock ( t->mutex );
( *( t->acount ) )--;
g_cond_signal ( t->cond );
g_mutex_unlock ( t->mutex );
}
static void check_is_ascii ( thread_state *t, G_GNUC_UNUSED gpointer user_data )
{
for ( unsigned int i = t->start; i < t->stop; i++ ) {
t->state->lines_not_ascii[i] = is_not_ascii ( t->state->lines[i] );
}
g_mutex_lock ( t->mutex );
( *( t->acount ) )--;
g_cond_signal ( t->cond );
g_mutex_unlock ( t->mutex );
}
/**
* Small wrapper function to create easy workers.
*/
static void call_thread ( gpointer data, gpointer user_data )
{
thread_state *t = (thread_state *) data;
t->callback ( t, user_data );
}
static void menu_refilter ( MenuState *state )
{
TICK_N ( "Filter start" )
if ( strlen ( state->text->text ) > 0 ) {
unsigned int j = 0;
char **tokens = tokenize ( state->text->text, config.case_sensitive );
/**
* On long lists it can be beneficial to parallelize.
* If number of threads is 1, no thread is spawn.
* If number of threads > 1 and there are enough (> 20000) items, spawn threads.
* If number of threads > 1 and there are enough (> 1000) items, spawn jobs for the thread pool.
* For large lists with 8 threads I see a factor three speedup of the whole function.
*/
unsigned int nt = MAX ( 1, MIN ( state->num_lines / 1000, config.threads ) );
unsigned int nt = MAX ( 1, state->num_lines / 500 );
thread_state states[nt];
GThread *threads[nt];
GCond cond;
GMutex mutex;
g_mutex_init ( &mutex );
g_cond_init ( &cond );
unsigned int count = nt;
unsigned int steps = ( state->num_lines + nt ) / nt;
for ( unsigned int i = 0; i < nt; i++ ) {
states[i].state = state;
states[i].tokens = tokens;
states[i].start = i * steps;
states[i].stop = MIN ( state->num_lines, ( i + 1 ) * steps );
states[i].count = 0;
states[i].state = state;
states[i].tokens = tokens;
states[i].start = i * steps;
states[i].stop = MIN ( state->num_lines, ( i + 1 ) * steps );
states[i].count = 0;
states[i].cond = &cond;
states[i].mutex = &mutex;
states[i].acount = &count;
states[i].callback = filter_elements;
if ( i > 0 ) {
threads[i] = g_thread_new ( NULL, filter_elements, &( states[i] ) );
g_thread_pool_push ( tpool, &states[i], NULL );
}
}
// Run one in this thread.
filter_elements ( &states[0] );
for ( unsigned int i = 1; i < nt; i++ ) {
g_thread_join ( threads[i] );
filter_elements ( &states[0], NULL );
// No need to do this with only one thread.
if ( nt > 1 ) {
g_mutex_lock ( &mutex );
while ( count > 0 ) {
g_cond_wait ( &cond, &mutex );
}
g_mutex_unlock ( &mutex );
}
g_cond_clear ( &cond );
g_mutex_clear ( &mutex );
for ( unsigned int i = 0; i < nt; i++ ) {
if ( j != states[i].start ) {
memcpy ( &( state->line_map[j] ), &( state->line_map[states[i].start] ), sizeof ( unsigned int ) * ( states[i].count ) );
@ -800,6 +842,7 @@ static void menu_refilter ( MenuState *state )
scrollbar_set_max_value ( state->scrollbar, state->filtered_lines );
state->refilter = FALSE;
state->rchanged = TRUE;
TICK_N ( "Filter done" )
}
static void menu_draw ( MenuState *state, cairo_t *d )
@ -882,8 +925,9 @@ static void menu_draw ( MenuState *state, cairo_t *d )
static void menu_update ( MenuState *state )
{
cairo_surface_t *surf = cairo_image_surface_create ( get_format (), state->w, state->h );
cairo_t *d = cairo_create ( surf );
TICK ()
cairo_surface_t * surf = cairo_image_surface_create ( get_format (), state->w, state->h );
cairo_t *d = cairo_create ( surf );
cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE );
if ( config.fake_transparency ) {
if ( state->bg != NULL ) {
@ -901,6 +945,7 @@ static void menu_update ( MenuState *state )
color_background ( display, d );
cairo_paint ( d );
}
TICK_N ( "Background" )
color_border ( display, d );
if ( config.menu_bw > 0 ) {
@ -972,6 +1017,7 @@ static void menu_update ( MenuState *state )
// Flush the surface.
cairo_surface_flush ( surface );
TICK ()
}
/**
@ -1066,7 +1112,8 @@ static void menu_resize ( MenuState *state )
MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *selected_line, unsigned int *next_pos, const char *message )
{
int shift = FALSE;
TICK ()
int shift = FALSE;
MenuState state = {
.sw = sw,
.selected_line = selected_line,
@ -1093,9 +1140,40 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
// find out which lines contain non-ascii codepoints, so we can be faster in some cases.
if ( state.lines != NULL ) {
for ( unsigned int line = 0; state.lines[line]; line++ ) {
state.lines_not_ascii[line] = is_not_ascii ( state.lines[line] );
TICK_N ( "Is ASCII start" )
unsigned int nt = MAX ( 1, state.num_lines / 5000 );
thread_state states[nt];
unsigned int steps = ( state.num_lines + nt ) / nt;
unsigned int count = nt;
GCond cond;
GMutex mutex;
g_mutex_init ( &mutex );
g_cond_init ( &cond );
for ( unsigned int i = 0; i < nt; i++ ) {
states[i].state = &state;
states[i].start = i * steps;
states[i].stop = MIN ( ( i + 1 ) * steps, state.num_lines );
states[i].acount = &count;
states[i].mutex = &mutex;
states[i].cond = &cond;
states[i].callback = check_is_ascii;
if ( i > 0 ) {
g_thread_pool_push ( tpool, &( states[i] ), NULL );
}
}
// Run one in this thread.
check_is_ascii ( &( states[0] ), NULL );
// No need to do this with only one thread.
if ( nt > 1 ) {
g_mutex_lock ( &mutex );
while ( count > 0 ) {
g_cond_wait ( &cond, &mutex );
}
g_mutex_unlock ( &mutex );
}
g_cond_clear ( &cond );
g_mutex_clear ( &mutex );
TICK_N ( "Is ASCII stop" )
}
if ( next_pos ) {
@ -1111,6 +1189,7 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
// Break off.
return MENU_CANCEL;
}
TICK_N ( "Grab keyboard" )
// main window isn't explicitly destroyed in case we switch modes. Reusing it prevents flicker
XWindowAttributes attr;
if ( main_window == None || XGetWindowAttributes ( display, main_window, &attr ) == 0 ) {
@ -1119,8 +1198,10 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
sn_launchee_context_setup_window ( sncontext, main_window );
}
}
TICK_N ( "Startup notification" )
// Get active monitor size.
monitor_active ( display, &( state.mon ) );
TICK_N ( "Get active monitor" )
if ( config.fake_transparency ) {
Window root = DefaultRootWindow ( display );
int screen = DefaultScreen ( display );
@ -1136,6 +1217,7 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
cairo_paint ( dr );
cairo_destroy ( dr );
cairo_surface_destroy ( s );
TICK_N ( "Fake transparency" )
}
// we need this at this point so we can get height.
@ -1299,6 +1381,7 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
}
// Get next event. (might block)
XNextEvent ( display, &ev );
TICK_N ( "X Event" )
if ( sndisplay != NULL ) {
sn_display_process_event ( sndisplay, &ev );
}
@ -1759,19 +1842,15 @@ static void print_main_application_options ( void )
printf ( "\t"color_bold "-display [string]"color_reset " X server to contact.\n" );
printf ( "\t\t"color_italic "${DISPLAY}"color_reset "\n" );
printf ( "\t"color_bold "-h,-help"color_reset " This help message.\n" );
printf (
"\t"color_bold "-dump-xresources"color_reset
" Dump the current configuration in Xresources format and exit.\n" );
printf (
"\t"color_bold "-e [string]"color_reset
" Show a dialog displaying the passed message and exit.\n" );
printf ( "\t"color_bold "-dump-xresources"color_reset
" Dump the current configuration in Xresources format and exit.\n" );
printf ( "\t"color_bold "-e [string]"color_reset
" Show a dialog displaying the passed message and exit.\n" );
printf ( "\t"color_bold "-markup"color_reset " Enable pango markup where possible.\n" );
printf (
"\t"color_bold "-normal-window"color_reset
" In dmenu mode, behave as a normal window. (experimental)\n" );
printf (
"\t"color_bold "-show [mode]"color_reset
" Show the mode 'mode' and exit. The mode has to be enabled.\n" );
printf ( "\t"color_bold "-normal-window"color_reset
" In dmenu mode, behave as a normal window. (experimental)\n" );
printf ( "\t"color_bold "-show [mode]"color_reset
" Show the mode 'mode' and exit. The mode has to be enabled.\n" );
}
else {
printf ( "\t-no-config Do not load configuration, use default values.\n" );
@ -1810,6 +1889,10 @@ static void help ( G_GNUC_UNUSED int argc, char **argv )
*/
static void cleanup ()
{
if ( tpool ) {
g_thread_pool_free ( tpool, TRUE, FALSE );
tpool = NULL;
}
// Cleanup
if ( display != NULL ) {
if ( main_window != None ) {
@ -1850,6 +1933,8 @@ static void cleanup ()
// Cleanup the custom keybinding
cleanup_abe ();
TIMINGS_STOP ()
}
/**
@ -2171,6 +2256,8 @@ static void error_trap_pop ( G_GNUC_UNUSED SnDisplay *display, Display *xdispl
int main ( int argc, char *argv[] )
{
TIMINGS_START ()
cmd_set_arguments ( argc, argv );
// Quiet flag
int quiet = ( find_arg ( "-quiet" ) >= 0 );
@ -2195,7 +2282,7 @@ int main ( int argc, char *argv[] )
// Free the basename for dmenu detection.
g_free ( base_name );
}
TICK ()
// Get the path to the cache dir.
cache_dir = g_get_user_cache_dir ();
@ -2206,9 +2293,11 @@ int main ( int argc, char *argv[] )
}
config_parser_add_option ( xrm_String, "pid", (void * *) &pidfile, "Pidfile location" );
TICK ()
// Register cleanup function.
atexit ( cleanup );
TICK ()
// Get DISPLAY, first env, then argument.
display_str = getenv ( "DISPLAY" );
find_arg_str ( "-display", &display_str );
@ -2225,15 +2314,17 @@ int main ( int argc, char *argv[] )
fprintf ( stderr, "cannot open display!\n" );
return EXIT_FAILURE;
}
TICK_N ( "Open Display" )
// startup not.
sndisplay = sn_display_new ( display, error_trap_push, error_trap_pop );
if ( sndisplay != NULL ) {
sncontext = sn_launchee_context_new_from_environment ( sndisplay, DefaultScreen ( display ) );
}
TICK_N ( "Startup Notification" )
// Setup keybinding
setup_abe ();
TICK_N ( "Setup abe" )
if ( find_arg ( "-no-config" ) < 0 ) {
load_configuration ( display );
@ -2254,8 +2345,10 @@ int main ( int argc, char *argv[] )
x11_setup ( display );
TICK_N ( "X11 Setup " )
// Sanity check
config_sanity_check ( display );
TICK_N ( "Config sanity check" )
// Dump.
// catch help request
if ( find_arg ( "-h" ) >= 0 || find_arg ( "-help" ) >= 0 || find_arg ( "--help" ) >= 0 ) {
@ -2268,7 +2361,7 @@ int main ( int argc, char *argv[] )
}
// Parse the keybindings.
parse_keys_abe ();
TICK_N ( "Parse ABE" )
char *msg = NULL;
if ( find_arg_str ( "-e", &( msg ) ) ) {
int markup = FALSE;
@ -2278,6 +2371,12 @@ int main ( int argc, char *argv[] )
return show_error_message ( msg, markup );
}
// Create thread pool
tpool = g_thread_pool_new ( call_thread, NULL, config.threads, FALSE, NULL );
g_thread_pool_set_max_idle_time ( 60000 );
g_thread_pool_set_max_unused_threads ( config.threads );
TICK_N ( "Setup Threadpool" )
// Dmenu mode.
if ( dmenu_mode == TRUE ) {
normal_window_mode = find_arg ( "-normal-window" ) >= 0;