Check our capture filter syntax in a separate thread.
authorgerald <gerald@f5534014-38df-0310-8fa8-9805f1628bb7>
Mon, 10 Oct 2011 22:39:35 +0000 (22:39 +0000)
committergerald <gerald@f5534014-38df-0310-8fa8-9805f1628bb7>
Mon, 10 Oct 2011 22:39:35 +0000 (22:39 +0000)
git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@39349 f5534014-38df-0310-8fa8-9805f1628bb7

gtk/capture_dlg.c
gtk/capture_dlg.h
gtk/main.c

index 8a3a61a689e19e5bc311943ca6cad1386a15408b..6980a1779e94054b48e99a9b493c8495b1c9d9e9 100644 (file)
@@ -266,43 +266,207 @@ capture_get_cap_settings (gchar *if_name)
   return cap_settings;
 }
 
+enum cfc_state_t {
+  CFC_PENDING,
+  CFC_UNKNOWN,
+  CFC_VALID,
+  CFC_INVALID
+};
+
+typedef struct capture_filter_check {
+  enum cfc_state_t state;
+  gchar *filter_text;
+  GtkWidget *filter_te;
+  int dlt;
+} capture_filter_check_t;
+
+/* Valid states:
+ *
+ * Idle: filter_text = NULL, state = ?
+ * Pending: filter_text != NULL, state = CFC_PENDING
+ * Unknown: filter_text != NULL, state = CFC_UNKNOWN
+ * Known: filter_text != NULL, state = CFC_VALID || CFC_INVALID
+ *
+ * We assume that only one text entry is active at a time.
+ */
+
+/* We could make this smarter by caching results */
+capture_filter_check_t cfc_data;
+
+#ifdef USE_THREADS
+GMutex *pcap_compile_mtx = NULL;
+GCond *cfc_data_cond = NULL;
+GMutex *cfc_data_mtx = NULL;
+GThread *cfc_thread = NULL;
+#endif
+
+#if 0
+#define DEBUG_SYNTAX_CHECK(state1, state2) g_warning("CF state %s -> %s : %s", state1, state2, cfc_data.filter_text)
+#else
+#define DEBUG_SYNTAX_CHECK(state1, state2)
+#endif
+
+static void *
+check_capture_filter_syntax(void *data _U_) {
+  struct bpf_program fcode;
+  int pc_err;
+
+#ifdef USE_THREADS
+  while (1) {
+    g_mutex_lock(cfc_data_mtx);
+    while (!cfc_data.filter_text || cfc_data.state != CFC_PENDING) {
+      /* Do we really need to use a mutex here? We only have one thread... */
+      g_cond_wait(cfc_data_cond, cfc_data_mtx);
+    }
+#endif
+    cfc_data.state = CFC_UNKNOWN;
+    DEBUG_SYNTAX_CHECK("pending", "unknown");
+#ifdef USE_THREADS
+    g_mutex_unlock(cfc_data_mtx);
+    g_mutex_lock(pcap_compile_mtx);
+#endif
+    /* pcap_compile_nopcap will not alter the filter string, so the (char *) cast is "safe" */
+    pc_err = pcap_compile_nopcap(DUMMY_SNAPLENGTH /* use a dummy snaplength for syntax-checking */,
+            cfc_data.dlt, &fcode, cfc_data.filter_text, 1 /* Do optimize */,
+            DUMMY_NETMASK /* use a dummy netmask for syntax-checking */);
+#ifdef USE_THREADS
+    g_mutex_unlock(pcap_compile_mtx);
+    g_mutex_lock(cfc_data_mtx);
+#endif
+    if (cfc_data.state == CFC_UNKNOWN) { /* No more input came in */
+      if (pc_err) {
+        DEBUG_SYNTAX_CHECK("unknown", "known bad");
+        cfc_data.state = CFC_INVALID;
+      } else {
+        DEBUG_SYNTAX_CHECK("unknown", "known good");
+        cfc_data.state = CFC_VALID;
+      }
+    }
+#ifdef USE_THREADS
+    g_mutex_unlock(cfc_data_mtx);
+  }
+#endif
+  return NULL;
+}
+
+static gboolean
+update_capture_filter_te(gpointer data _U_) {
+
+  if (!prefs.capture_syntax_check_filter)
+    return TRUE;
+
+#ifdef USE_THREADS
+  g_mutex_lock(cfc_data_mtx);
+#endif
+  if (cfc_data.filter_text && cfc_data.filter_te) {
+    if (cfc_data.state == CFC_VALID) {
+      colorize_filter_te_as_valid(cfc_data.filter_te);
+    } else if (cfc_data.state == CFC_INVALID) {
+      colorize_filter_te_as_invalid(cfc_data.filter_te);
+    } else {
+      colorize_filter_te_as_empty(cfc_data.filter_te);
+    }
+
+    if (cfc_data.state == CFC_VALID || cfc_data.state == CFC_INVALID) {
+        DEBUG_SYNTAX_CHECK("known", "idle");
+      /* Reset the current state to idle. */
+      if (cfc_data.filter_text != NULL) {
+        g_free(cfc_data.filter_text);
+      }
+      cfc_data.filter_text = NULL;
+      cfc_data.state = CFC_PENDING;
+    }
+  }
+#ifdef USE_THREADS
+  g_mutex_unlock(cfc_data_mtx);
+#endif
+  return TRUE;
+}
+
+/** Initialize background capture filter syntax checking
+ */
+void capture_filter_init(void) {
+  cfc_data.filter_text = NULL;
+  cfc_data.filter_te = NULL;
+  cfc_data.state = CFC_PENDING;
+#ifdef USE_THREADS
+  pcap_compile_mtx = g_mutex_new();
+  cfc_data_cond = g_cond_new();
+  cfc_data_mtx = g_mutex_new();
+  g_timeout_add(200, update_capture_filter_te, NULL);
+  g_thread_create(check_capture_filter_syntax, NULL, FALSE, NULL);
+#endif
+}
+
 static void
 capture_filter_check_syntax_cb(GtkWidget *w _U_, gpointer user_data _U_)
 {
-  struct bpf_program fcode;
-  GtkWidget *filter_cm, *filter_te;
-  const gchar *filter_text;
-  gpointer  ptr;
-  int       dlt;
+  GtkWidget *filter_cm, *filter_te, *linktype_combo_box;
+  gchar *filter_text;
+  gpointer dlt_ptr;
 
-  GtkWidget *linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
+  if (!prefs.capture_syntax_check_filter)
+    return;
 
-  if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &ptr)) {
+  linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
+
+  if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &dlt_ptr)) {
     g_assert_not_reached();  /* Programming error: somehow nothing is active */
   }
-  if ((dlt = GPOINTER_TO_INT(ptr)) == -1) {
+  if ((cfc_data.dlt = GPOINTER_TO_INT(dlt_ptr)) == -1) {
     g_assert_not_reached();  /* Programming error: somehow managed to select an "unsupported" entry */
   }
 
-  if (!prefs.capture_syntax_check_filter)
-    return;
-
   filter_cm = g_object_get_data(G_OBJECT(opt_edit_w), E_CFILTER_CM_KEY);
+  if (!filter_cm)
+    return;
   filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
+  if (!filter_te)
+    return;
+
   filter_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(filter_cm));
+
   if (strlen(filter_text) == 0) {
     colorize_filter_te_as_empty(filter_te);
     return;
   }
 
-  /* pcap_compile_nopcap will not alter the filter string, so the (char *) cast is "safe" */
-  if (pcap_compile_nopcap(DUMMY_SNAPLENGTH /* use a dummy snaplength for syntax-checking */,
-          dlt, &fcode, (char *)filter_text, 1 /* Do optimize */,
-          DUMMY_NETMASK /* use a dummy netmask for syntax-checking */) < 0) {
-    colorize_filter_te_as_invalid(filter_te);
-  } else {
-    colorize_filter_te_as_valid(filter_te);
+#ifdef USE_THREADS
+  g_mutex_lock(cfc_data_mtx);
+#endif
+  /* Ruthlessly clobber the current state. */
+  if (cfc_data.filter_text != NULL) {
+    g_free(cfc_data.filter_text);
+  }
+  cfc_data.filter_text = filter_text;
+  cfc_data.filter_te = filter_te;
+  cfc_data.state = CFC_PENDING;
+  DEBUG_SYNTAX_CHECK("?", "pending");
+#ifdef USE_THREADS
+  g_cond_signal(cfc_data_cond);
+  g_mutex_unlock(cfc_data_mtx);
+#else
+  check_capture_filter_syntax(NULL);
+  update_capture_filter_te(NULL);
+#endif
+}
+
+static void
+capture_filter_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
+{
+#ifdef USE_THREADS
+  g_mutex_lock(cfc_data_mtx);
+#endif
+  /* Reset the current state to idle. */
+  if (cfc_data.filter_text != NULL) {
+    g_free(cfc_data.filter_text);
   }
+  cfc_data.filter_text = NULL;
+  cfc_data.filter_te = NULL;
+  cfc_data.state = CFC_PENDING;
+#ifdef USE_THREADS
+  g_mutex_unlock(cfc_data_mtx);
+#endif
 }
 
 #define TIME_UNIT_SECOND    0
@@ -1474,11 +1638,17 @@ capture_filter_compile_cb(GtkWidget *w _U_, gpointer user_data _U_)
   pd = pcap_open_dead(dlt, DUMMY_SNAPLENGTH);
   filter_cm = g_object_get_data(G_OBJECT(opt_edit_w), E_CFILTER_CM_KEY);
   filter_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(filter_cm));
+#ifdef USE_THREADS
+    g_mutex_lock(pcap_compile_mtx);
+#endif
   /* pcap_compile will not alter the filter string, so the (char *) cast is "safe" */
 #ifdef PCAP_NETMASK_UNKNOWN
   if (pcap_compile(pd, &fcode, (char *)filter_text, 1 /* Do optimize */, PCAP_NETMASK_UNKNOWN) < 0) {
 #else
   if (pcap_compile(pd, &fcode, (char *)filter_text, 1 /* Do optimize */, 0) < 0) {
+#endif
+#ifdef USE_THREADS
+    g_mutex_unlock(pcap_compile_mtx);
 #endif
     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", pcap_geterr(pd));
   } else {
@@ -2052,6 +2222,7 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum
   filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
   colorize_filter_te_as_empty(filter_te);
   g_signal_connect(filter_te, "changed", G_CALLBACK(capture_filter_check_syntax_cb), NULL);
+  g_signal_connect(filter_te, "destroy", G_CALLBACK(capture_filter_destroy_cb), NULL);
 
   for (cf_entry = cfilter_list; cf_entry != NULL; cf_entry = g_list_next(cf_entry)) {
     if (cf_entry->data && (strlen(cf_entry->data) > 0)) {
index 2cb006486f0b2f52d7a4c3a21c88d71880b81339..c7825965044d94eae7e40b1b6acf026d08fb3812 100644 (file)
@@ -100,6 +100,10 @@ enum
     NUM_COLUMNS
 };
 
+/** Initialize background capture filter syntax checking
+ */
+void capture_filter_init(void);
+
 /** User requested the "Capture Options" dialog box by menu or toolbar.
  *
  * @param widget parent widget (unused)
@@ -160,10 +164,10 @@ typedef struct {
 cap_settings_t
 capture_get_cap_settings (gchar *if_name);
 
-GtkTreeModel* 
+GtkTreeModel*
 create_and_fill_model (GtkTreeView *view);
 
-gboolean 
+gboolean
 query_tooltip_tree_view_cb (GtkWidget  *widget,
                             gint        x,
                             gint        y,
@@ -171,7 +175,7 @@ query_tooltip_tree_view_cb (GtkWidget  *widget,
                             GtkTooltip *tooltip,
                             gpointer    data);
 
-void 
+void
 activate_monitor (GtkTreeViewColumn *tree_column, GtkCellRenderer *renderer,
                   GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data);
 
@@ -190,7 +194,7 @@ capture_remote_combo_recent_write_all(FILE *rf);
  * @param s string with hostname,port,auth_type
  * @return TRUE if correctly added
  */
-gboolean 
+gboolean
 capture_remote_combo_add_recent(gchar *s);
 #endif
 
index d40bae7ba588dcf58688b1686a8c980199ef964d..492c7529a86240e27d026997889137ced562bd70 100644 (file)
@@ -1008,7 +1008,7 @@ main_window_delete_event_cb(GtkWidget *widget _U_, GdkEvent *event _U_, gpointer
   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
     gtk_window_present(GTK_WINDOW(top_level));
     /* user didn't saved his current file, ask him */
-    dialog = simple_dialog(ESD_TYPE_CONFIRMATION, 
+    dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
                 ((cfile.state == FILE_READ_IN_PROGRESS) ? ESD_BTNS_QUIT_DONTSAVE_CANCEL : ESD_BTNS_SAVE_QUIT_DONTSAVE_CANCEL),
                 "%sSave capture file before program quit?%s\n\n"
                 "If you quit the program without saving, your capture data will be discarded.",
@@ -1115,7 +1115,7 @@ file_quit_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
 
   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
        /* user didn't saved his current file, ask him */
-       dialog = simple_dialog(ESD_TYPE_CONFIRMATION, 
+       dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
                   ((cfile.state == FILE_READ_IN_PROGRESS) ? ESD_BTNS_QUIT_DONTSAVE_CANCEL : ESD_BTNS_SAVE_QUIT_DONTSAVE_CANCEL),
                    "%sSave capture file before program quit?%s\n\n"
                    "If you quit the program without saving, your capture data will be discarded.",
@@ -2917,6 +2917,7 @@ main(int argc, char *argv[])
 
   color_filters_init();
   decode_as_init();
+  capture_filter_init();
 
   /* the window can be sized only, if it's not already shown, so do it now! */
   main_load_window_geometry(top_level);