Merge lp:~cjcurran/indicator-sound/stable-service-with-unstable-pulse into lp:indicator-sound/0.1

Proposed by Conor Curran
Status: Merged
Merged at revision: not available
Proposed branch: lp:~cjcurran/indicator-sound/stable-service-with-unstable-pulse
Merge into: lp:indicator-sound/0.1
Diff against target: 239 lines (+106/-20)
2 files modified
src/pulse-manager.c (+51/-12)
src/sound-service.c (+55/-8)
To merge this branch: bzr merge lp:~cjcurran/indicator-sound/stable-service-with-unstable-pulse
Reviewer Review Type Date Requested Status
David Barth Needs Information
Review via email: mp+20224@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Conor Curran (cjcurran) wrote :

This work should make the service stable against a flapping pulseaudio daemon. Tested thoroughly with an eye on memory using pmap on the service pid (connecting and reconnecting to pulse can cause memory leaks).

Also caches any new sinks that become available during its lifespan. This may prevent a seg fault that was reported earlier this week.

Revision history for this message
David Barth (dbarth) wrote :

I'd like to see some unit tests that show how the code now handles corner cases properly, typically with pa not being available at startup, then appearing, the flipping ten times. Do you have a test environment that can simulate that situation?

To improve the readability of the merge proposals, can you also embed some emacs or vi tab setttings in your files?

review: Needs Information

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/pulse-manager.c'
2--- src/pulse-manager.c 2010-02-18 14:30:43 +0000
3+++ src/pulse-manager.c 2010-02-26 14:45:26 +0000
4@@ -31,7 +31,6 @@
5
6 static GHashTable *sink_hash = NULL;
7 static SoundServiceDbus *dbus_service = NULL;
8-// Until we find a satisfactory default sink this index should remain < 0
9 static gint DEFAULT_SINK_INDEX = -1;
10 static gboolean pa_server_available = FALSE;
11 // PA related
12@@ -46,6 +45,7 @@
13 static void pulse_source_info_callback(pa_context *c, const pa_source_info *i, int eol, void *userdata);
14 static void destroy_sink_info(void *value);
15 static gboolean determine_sink_availability();
16+static void reconnect_to_pulse();
17
18
19 /**
20@@ -65,14 +65,19 @@
21 g_assert(pulse_context);
22
23 sink_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, destroy_sink_info);
24+
25 // Establish event callback registration
26 pa_context_set_state_callback(pulse_context, context_state_callback, NULL);
27- pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
28+ // BUILD MENU ANYWHO - it will be updated
29+ update_pa_state(FALSE, FALSE, FALSE, 0);
30+
31+ pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL);
32 }
33
34 void close_pulse_activites()
35 {
36- if (pulse_context){
37+ if (pulse_context != NULL){
38+ g_debug("freeing the pulse context");
39 pa_context_unref(pulse_context);
40 pulse_context = NULL;
41 }
42@@ -82,6 +87,30 @@
43 g_debug("I just closed communication with Pulse");
44 }
45
46+/**
47+reconnect_to_pulse()
48+In the event of Pulseaudio flapping in the wind handle gracefully without
49+memory leaks !
50+*/
51+static void reconnect_to_pulse()
52+{
53+ // reset
54+ if (pulse_context != NULL){
55+ g_debug("freeing the pulse context");
56+ pa_context_unref(pulse_context);
57+ pulse_context = NULL;
58+ }
59+ g_hash_table_destroy(sink_hash);
60+
61+ // reconnect
62+ pulse_context = pa_context_new(pa_glib_mainloop_get_api(pa_main_loop), "ayatana.indicator.sound");
63+ g_assert(pulse_context);
64+ sink_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, destroy_sink_info);
65+ // Establish event callback registration
66+ pa_context_set_state_callback(pulse_context, context_state_callback, NULL);
67+ update_pa_state(FALSE, FALSE, FALSE, 0);
68+ pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL);
69+}
70
71 static void destroy_sink_info(void *value)
72 {
73@@ -186,6 +215,8 @@
74 */
75 void set_sink_volume(gdouble percent)
76 {
77+ if(pa_server_available == FALSE)
78+ return;
79 g_debug("in the pulse manager:set_sink_volume with percent %f", percent);
80 if(DEFAULT_SINK_INDEX < 0)
81 {
82@@ -366,12 +397,20 @@
83 }
84 else
85 {
86- // TODO ADD new sink - part of big refactor
87- g_debug("attempting to add new sink with name %s", info->name);
88- //sink_info *s;
89- //s = g_new0(sink_info, 1);
90- //update the sinks hash with new sink.
91- }
92+ sink_info *value;
93+ value = g_new0(sink_info, 1);
94+ value->index = value->device_index = info->index;
95+ value->name = g_strdup(info->name);
96+ value->description = g_strdup(info->description);
97+ value->icon_name = g_strdup(pa_proplist_gets(info->proplist, PA_PROP_DEVICE_ICON_NAME));
98+ value->active_port = (info->active_port != NULL);
99+ value->mute = !!info->mute;
100+ value->volume = info->volume;
101+ value->base_volume = info->base_volume;
102+ value->channel_map = info->channel_map;
103+ g_hash_table_insert(sink_hash, GINT_TO_POINTER(value->index), value);
104+ g_debug("pulse-manager:update_sink_info -> After adding a new sink to our hash");
105+ }
106 }
107
108
109@@ -474,7 +513,7 @@
110 g_debug("unconnected");
111 break;
112 case PA_CONTEXT_CONNECTING:
113- g_debug("connecting");
114+ g_debug("connecting - waiting for the server to become available");
115 break;
116 case PA_CONTEXT_AUTHORIZING:
117 g_debug("authorizing");
118@@ -484,8 +523,8 @@
119 break;
120 case PA_CONTEXT_FAILED:
121 g_warning("FAILED to retrieve context - Is PulseAudio Daemon running ?");
122- //Update the indicator to show PA either is not ready or has no available sink
123- update_pa_state(FALSE, FALSE, TRUE, 0);
124+ pa_server_available = FALSE;
125+ reconnect_to_pulse();
126 break;
127 case PA_CONTEXT_TERMINATED:
128 g_debug("context terminated");
129
130=== modified file 'src/sound-service.c'
131--- src/sound-service.c 2010-02-18 11:11:38 +0000
132+++ src/sound-service.c 2010-02-26 14:45:26 +0000
133@@ -37,12 +37,13 @@
134 static gboolean b_sink_available = FALSE;
135 static gboolean b_all_muted = FALSE;
136 static gboolean b_pulse_ready = FALSE;
137+static gboolean b_startup = TRUE;
138 static gdouble volume_percent = 0.0;
139
140 static void set_global_mute_from_ui();
141 static gboolean idle_routine (gpointer data);
142 static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service);
143-
144+static void refresh_menu();
145
146 /**********************************************************************************************************************/
147 // Init functions (GTK and DBUS)
148@@ -66,7 +67,8 @@
149 }
150 }
151 /**
152-Build the DBus menu items. For now Mute all/Unmute is the only available option
153+rebuild_sound_menu:
154+Build the DBus menu items, mute/unmute, slider, separator and sound preferences 'link'
155 **/
156 static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service)
157 {
158@@ -80,10 +82,18 @@
159 volume_slider_menuitem = slider_menu_item_new(b_sink_available, volume_percent);
160 dbusmenu_menuitem_child_append(root, mute_all_menuitem);
161 dbusmenu_menuitem_child_append(root, DBUSMENU_MENUITEM(volume_slider_menuitem));
162-
163+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
164+ DBUSMENU_MENUITEM_PROP_ENABLED,
165+ b_sink_available);
166+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
167+ DBUSMENU_MENUITEM_PROP_VISIBLE,
168+ b_sink_available);
169+ // Separator
170 DbusmenuMenuitem *separator = dbusmenu_menuitem_new();
171 dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
172 dbusmenu_menuitem_child_append(root, separator);
173+
174+ // Sound preferences dialog
175 DbusmenuMenuitem *settings_mi = dbusmenu_menuitem_new();
176 dbusmenu_menuitem_property_set(settings_mi, DBUSMENU_MENUITEM_PROP_LABEL,
177 _("Sound Preferences..."));
178@@ -127,8 +137,8 @@
179 if (mainloop != NULL) {
180 g_debug("Service shutdown !");
181 // TODO: uncomment for release !!
182- close_pulse_activites();
183- g_main_loop_quit(mainloop);
184+/* close_pulse_activites();*/
185+/* g_main_loop_quit(mainloop);*/
186 }
187 return;
188 }
189@@ -140,12 +150,49 @@
190 b_pulse_ready = pa_state;
191 volume_percent = percent;
192 g_debug("update pa state with state %i, availability of %i, mute value of %i and a volume percent is %f", pa_state, sink_available, sink_muted, volume_percent);
193+ // Only rebuild the menu on start up...
194+ if(b_startup == TRUE){
195+ rebuild_sound_menu(root_menuitem, dbus_interface);
196+ b_startup = FALSE;
197+ }
198+ else{
199+ refresh_menu();
200+ }
201+ // Emit the signals after the menus are setup/torn down
202 sound_service_dbus_update_sink_volume(dbus_interface, percent);
203 sound_service_dbus_update_sink_mute(dbus_interface, sink_muted);
204
205- // Only rebuild the menu on start up...
206- if(volume_slider_menuitem == NULL)
207- rebuild_sound_menu(root_menuitem, dbus_interface);
208+}
209+
210+static void refresh_menu()
211+{
212+ g_debug("in the refresh menu method");
213+ if(b_sink_available == FALSE || b_pulse_ready == FALSE)
214+ {
215+
216+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
217+ DBUSMENU_MENUITEM_PROP_ENABLED,
218+ FALSE);
219+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
220+ DBUSMENU_MENUITEM_PROP_VISIBLE,
221+ FALSE);
222+ dbusmenu_menuitem_property_set_bool(mute_all_menuitem,
223+ DBUSMENU_MENUITEM_PROP_ENABLED,
224+ FALSE);
225+
226+ }
227+ else if(b_sink_available == TRUE && b_pulse_ready == TRUE){
228+
229+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
230+ DBUSMENU_MENUITEM_PROP_ENABLED,
231+ TRUE);
232+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
233+ DBUSMENU_MENUITEM_PROP_VISIBLE,
234+ TRUE);
235+ dbusmenu_menuitem_property_set_bool(mute_all_menuitem,
236+ DBUSMENU_MENUITEM_PROP_ENABLED,
237+ TRUE);
238+ }
239 }
240
241

Subscribers

People subscribed via source and target branches