diff options
Diffstat (limited to 'gio')
50 files changed, 3818 insertions, 2253 deletions
diff --git a/gio/Makefile.am b/gio/Makefile.am deleted file mode 100644 index 05b20cdef..000000000 --- a/gio/Makefile.am +++ /dev/null @@ -1,1016 +0,0 @@ -include $(top_srcdir)/glib.mk - -SUBDIRS = gdbus-2.0/codegen - -if OS_UNIX -SUBDIRS += xdgmime -endif - -if OS_WIN32_AND_DLL_COMPILATION -if MS_LIB_AVAILABLE -noinst_DATA += gio-2.0.lib - -install_ms_lib_cmd = $(INSTALL) gio-2.0.lib $(DESTDIR)$(libdir) -uninstall_ms_lib_cmd = -rm $(DESTDIR)$(libdir)/gio-2.0.lib -endif -endif - -install-ms-lib: - $(install_ms_lib_cmd) - -uninstall-ms-lib: - $(uninstall_ms_lib_cmd) - -AM_CPPFLAGS = \ - -DG_LOG_DOMAIN=\"GLib-GIO\" \ - $(gmodule_INCLUDES) \ - $(GLIB_DEBUG_FLAGS) \ - -DGIO_COMPILATION \ - -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" - -AM_CFLAGS = $(GLIB_WARN_CFLAGS) - -lib_LTLIBRARIES = libgio-2.0.la - -gdbus_headers = \ - gdbusauthobserver.h \ - gcredentials.h \ - gdbusutils.h \ - gdbuserror.h \ - gdbusaddress.h \ - gdbusconnection.h \ - gdbusmessage.h \ - gdbusnameowning.h \ - gdbusnamewatching.h \ - gdbusproxy.h \ - gdbusintrospection.h \ - gdbusmethodinvocation.h \ - gdbusserver.h \ - gdbusinterface.h \ - gdbusinterfaceskeleton.h \ - gdbusobject.h \ - gdbusobjectskeleton.h \ - gdbusobjectproxy.h \ - gdbusobjectmanager.h \ - gdbusobjectmanagerclient.h \ - gdbusobjectmanagerserver.h \ - gtestdbus.h \ - $(NULL) - -gdbus_sources = \ - gdbusutils.h gdbusutils.c \ - gdbusaddress.h gdbusaddress.c \ - gdbusauthobserver.h gdbusauthobserver.c \ - gdbusauth.h gdbusauth.c \ - gdbusauthmechanism.h gdbusauthmechanism.c \ - gdbusauthmechanismanon.h gdbusauthmechanismanon.c \ - gdbusauthmechanismexternal.h gdbusauthmechanismexternal.c \ - gdbusauthmechanismsha1.h gdbusauthmechanismsha1.c \ - gdbuserror.h gdbuserror.c \ - gdbusconnection.h gdbusconnection.c \ - gdbusmessage.h gdbusmessage.c \ - gdbusnameowning.h gdbusnameowning.c \ - gdbusnamewatching.h gdbusnamewatching.c \ - gdbusproxy.h gdbusproxy.c \ - gdbusprivate.h gdbusprivate.c \ - gdbusintrospection.h gdbusintrospection.c \ - gdbusmethodinvocation.h gdbusmethodinvocation.c \ - gdbusserver.h gdbusserver.c \ - gdbusinterface.h gdbusinterface.c \ - gdbusinterfaceskeleton.h gdbusinterfaceskeleton.c \ - gdbusobject.h gdbusobject.c \ - gdbusobjectskeleton.h gdbusobjectskeleton.c \ - gdbusobjectproxy.h gdbusobjectproxy.c \ - gdbusobjectmanager.h gdbusobjectmanager.c \ - gdbusobjectmanagerclient.h gdbusobjectmanagerclient.c \ - gdbusobjectmanagerserver.h gdbusobjectmanagerserver.c \ - gtestdbus.h gtestdbus.c \ - $(NULL) - -# These are not built into the library yet -EXTRA_DIST += gdbusdaemon.c gdbusdaemon.h dbus-daemon.xml - -GDBUS_PYTHON_DEPS = \ - $(srcdir)/gdbus-2.0/codegen/gdbus-codegen.in \ - $(srcdir)/gdbus-2.0/codegen/codegen_main.py \ - $(srcdir)/gdbus-2.0/codegen/parser.py \ - $(srcdir)/gdbus-2.0/codegen/codegen_docbook.py \ - $(srcdir)/gdbus-2.0/codegen/codegen.py \ - $(srcdir)/gdbus-2.0/codegen/__init__.py \ - $(srcdir)/gdbus-2.0/codegen/dbustypes.py \ - $(builddir)/gdbus-2.0/codegen/config.py \ - $(srcdir)/gdbus-2.0/codegen/utils.py - -gdbus-daemon-generated.h gdbus-daemon-generated.c: $(srcdir)/dbus-daemon.xml $(GDBUS_PYTHON_DEPS) - $(AM_V_GEN) UNINSTALLED_GLIB_SRCDIR=$(top_srcdir) \ - UNINSTALLED_GLIB_BUILDDIR=$(top_builddir) \ - $(PYTHON) $(srcdir)/gdbus-2.0/codegen/gdbus-codegen.in \ - --interface-prefix org. \ - --generate-c-code gdbus-daemon-generated \ - --c-namespace _G \ - $(srcdir)/dbus-daemon.xml \ - $(NULL) - -settings_headers = \ - gsettingsbackend.h \ - gsettingsschema.h \ - gsettings.h - -settings_base_sources = \ - gvdb/gvdb-format.h \ - gvdb/gvdb-reader.h \ - gvdb/gvdb-reader.c \ - gdelayedsettingsbackend.h \ - gdelayedsettingsbackend.c \ - gkeyfilesettingsbackend.c \ - gmemorysettingsbackend.c \ - gnullsettingsbackend.c \ - gsettingsbackendinternal.h \ - gsettingsbackend.c \ - gsettingsschema.h \ - gsettingsschema-internal.h \ - gsettingsschema.c \ - gsettings-mapping.h \ - gsettings-mapping.c \ - gsettings.c - -settings_sources = $(settings_base_sources) - -if OS_WIN32 -win32_settings_sources = \ - gregistrysettingsbackend.h \ - gregistrysettingsbackend.c - -settings_sources += $(win32_settings_sources) -endif - -application_headers = \ - gapplication.h \ - gapplicationcommandline.h \ - \ - gactiongroup.h \ - gactionmap.h \ - gsimpleactiongroup.h \ - gremoteactiongroup.h \ - gactiongroupexporter.h \ - gdbusactiongroup.h \ - gaction.h \ - gpropertyaction.h \ - gsimpleaction.h \ - \ - gmenumodel.h \ - gmenu.h \ - gmenuexporter.h \ - gdbusmenumodel.h \ - gnotification.h \ - $(NULL) - -application_sources = \ - gapplication.c \ - gapplicationcommandline.c \ - gapplicationimpl-dbus.c \ - gapplicationimpl.h \ - \ - gactiongroup.c \ - gactionmap.c \ - gsimpleactiongroup.c \ - gremoteactiongroup.c \ - gactiongroupexporter.c \ - gdbusactiongroup-private.h \ - gdbusactiongroup.c \ - gaction.c \ - gpropertyaction.c \ - gsimpleaction.c \ - \ - gmenumodel.c \ - gmenu.c \ - gmenuexporter.c \ - gdbusmenumodel.c \ - gnotification-private.h \ - gnotificationbackend.h \ - gnotification.c \ - gnotificationbackend.c \ - $(NULL) - -local_sources = \ - ghttpproxy.c \ - ghttpproxy.h \ - glocalfile.c \ - glocalfile.h \ - glocalfileprivate.h \ - glocalfileenumerator.c \ - glocalfileenumerator.h \ - glocalfileinfo.c \ - glocalfileinfo.h \ - glocalfileinputstream.c \ - glocalfileinputstream.h \ - glocalfilemonitor.c \ - glocalfilemonitor.h \ - glocalfileoutputstream.c \ - glocalfileoutputstream.h \ - glocalfileiostream.c \ - glocalfileiostream.h \ - glocalvfs.c \ - glocalvfs.h \ - gsocks4proxy.c \ - gsocks4proxy.h \ - gsocks4aproxy.c \ - gsocks4aproxy.h \ - gsocks5proxy.c \ - gsocks5proxy.h \ - thumbnail-verify.h \ - thumbnail-verify.c \ - $(NULL) - -platform_libadd = -platform_deps = -appinfo_sources = -appinfo_headers = -contenttype_sources = - -if HAVE_INOTIFY -SUBDIRS += inotify -platform_libadd += inotify/libinotify.la -platform_deps += inotify/libinotify.la -endif - -if HAVE_KQUEUE -SUBDIRS += kqueue -platform_libadd += kqueue/libkqueue.la -platform_deps += kqueue/libkqueue.la -endif - -if OS_WIN32 -SUBDIRS += win32 -platform_libadd += win32/libgiowin32.la -platform_deps += win32/libgiowin32.la -endif - -SUBDIRS += . tests - -if HAVE_FAM -SUBDIRS += fam -endif - -if OS_UNIX -platform_libadd += xdgmime/libxdgmime.la -platform_deps += xdgmime/libxdgmime.la -if !OS_COCOA -appinfo_headers += gdesktopappinfo.h -endif - - -unix_sources = \ - gfiledescriptorbased.c \ - gunixconnection.c \ - gunixcredentialsmessage.c \ - gunixfdlist.c \ - gunixfdmessage.c \ - gunixmount.c \ - gunixmount.h \ - gunixmounts.c \ - gunixsocketaddress.c \ - gunixvolume.c \ - gunixvolume.h \ - gunixvolumemonitor.c \ - gunixvolumemonitor.h \ - gunixinputstream.c \ - gunixoutputstream.c \ - gcontenttypeprivate.h \ - gfdonotificationbackend.c \ - ggtknotificationbackend.c \ - gportalnotificationbackend.c \ - gdocumentportal.c \ - gdocumentportal.h \ - gopenuriportal.c \ - gopenuriportal.h \ - gportalsupport.c \ - gportalsupport.h \ - $(portal_sources) \ - $(NULL) -nodist_unix_sources = \ - $(nodist_portal_sources) \ - $(NULL) - -giounixincludedir=$(includedir)/gio-unix-2.0/gio -giounixinclude_HEADERS = \ - gfiledescriptorbased.h \ - gunixconnection.h \ - gunixcredentialsmessage.h \ - gunixmounts.h \ - gunixfdlist.h \ - gunixfdmessage.h \ - gunixinputstream.h \ - gunixoutputstream.h \ - gunixsocketaddress.h \ - $(appinfo_headers) \ - $(NULL) - -if HAVE_NETLINK -unix_sources += \ - gnetworkmonitornetlink.c \ - gnetworkmonitornetlink.h \ - gnetworkmonitornm.c \ - gnetworkmonitornm.h \ - $(NULL) -endif -endif - -gdbus_daemon_sources = \ - gdbusdaemon.c \ - gdbusdaemon.h \ - $(NULL) -nodist_gdbus_daemon_sources = \ - gdbus-daemon-generated.c \ - gdbus-daemon-generated.h \ - $(NULL) - -win32_actual_sources = \ - $(gdbus_daemon_sources) \ - gwin32registrykey.c \ - gwin32registrykey.h \ - gwin32mount.c \ - gwin32mount.h \ - gwin32volumemonitor.c \ - gwin32volumemonitor.h \ - gwin32inputstream.c \ - gwin32outputstream.c \ - gwin32outputstream.h \ - gwin32networking.h \ - gwin32networkmonitor.c \ - gwin32networkmonitor.h \ - gwin32notificationbackend.c \ - $(NULL) -nodist_win32_actual_sources = \ - $(nodist_gdbus_daemon_sources) \ - $(NULL) - -win32_more_sources_for_vcproj = \ - gwin32appinfo.c \ - gcontenttype-win32.c \ - gregistrysettingsbackend.c \ - win32/gwinhttpfile.c \ - win32/gwinhttpfileinputstream.c \ - win32/gwinhttpfileoutputstream.c \ - win32/gwinhttpvfs.c \ - win32/gwin32fsmonitorutils.c \ - win32/gwin32filemonitor.c - -if OS_WIN32 -appinfo_sources += gwin32appinfo.c gwin32appinfo.h -contenttype_sources += gcontenttype-win32.c -platform_libadd += -lshlwapi -lws2_32 -ldnsapi -liphlpapi -win32_sources = $(win32_actual_sources) -nodist_win32_sources = $(nodist_win32_actual_sources) - -giowin32includedir=$(includedir)/gio-win32-2.0/gio -giowin32include_HEADERS = \ - gwin32inputstream.h \ - gwin32outputstream.h \ - $(NULL) - -endif - -xdp_dbus_built_sources = xdp-dbus.c xdp-dbus.h -BUILT_SOURCES += $(xdp_dbus_built_sources) -CLEANFILES += $(xdp_dbus_built_sources) - -portal_interfaces = \ - org.freedesktop.portal.Documents.xml \ - org.freedesktop.portal.OpenURI.xml \ - org.freedesktop.portal.ProxyResolver.xml \ - $(NULL) - -EXTRA_DIST += $(portal_interfaces) - -$(xdp_dbus_built_sources) : $(portal_interfaces) - $(AM_V_GEN) UNINSTALLED_GLIB_SRCDIR=$(top_srcdir) \ - UNINSTALLED_GLIB_BUILDDIR=$(top_builddir) \ - $(PYTHON) $(srcdir)/gdbus-2.0/codegen/gdbus-codegen.in \ - --interface-prefix org.freedesktop.portal. \ - --c-namespace GXdp \ - --generate-c-code $(builddir)/xdp-dbus \ - --annotate "org.freedesktop.portal.Documents.Add()" "org.gtk.GDBus.C.UnixFD" "true" \ - --annotate "org.freedesktop.portal.Documents.AddNamed()" "org.gtk.GDBus.C.UnixFD" "true" \ - --annotate "org.freedesktop.portal.Documents.AddFull()" "org.gtk.GDBus.C.UnixFD" "true" \ - --annotate "org.freedesktop.portal.OpenURI.OpenFile()" "org.gtk.GDBus.C.UnixFD" "true" \ - $^ - -portal_sources = \ - gnetworkmonitorportal.c \ - gnetworkmonitorportal.h \ - gproxyresolverportal.c \ - gproxyresolverportal.h \ - $(NULL) -nodist_portal_sources = \ - $(xdp_dbus_built_sources) \ - $(NULL) - -if OS_UNIX -if !OS_COCOA -contenttype_sources += gcontenttype.c -appinfo_sources += gdesktopappinfo.c -endif -endif - -gio_base_sources = \ - gappinfo.c \ - gappinfoprivate.h \ - gasynchelper.c \ - gasynchelper.h \ - gasyncinitable.c \ - gasyncresult.c \ - gbufferedinputstream.c \ - gbufferedoutputstream.c \ - gbytesicon.c \ - gcancellable.c \ - gcharsetconverter.c \ - gcontextspecificgroup.c \ - gcontextspecificgroup.h \ - gconverter.c \ - gconverterinputstream.c \ - gconverteroutputstream.c \ - gcredentials.c \ - gcredentialsprivate.h \ - gdatagrambased.c \ - gdatainputstream.c \ - gdataoutputstream.c \ - gdrive.c \ - gdummyfile.h \ - gdummyfile.c \ - gdummyproxyresolver.c \ - gdummyproxyresolver.h \ - gdummytlsbackend.c \ - gdummytlsbackend.h \ - gemblem.h \ - gemblem.c \ - gemblemedicon.h \ - gemblemedicon.c \ - gfile.c \ - gfileattribute.c \ - gfileattribute-priv.h \ - gfileenumerator.c \ - gfileicon.c \ - gfileinfo.c \ - gfileinfo-priv.h \ - gfileinputstream.c \ - gfilemonitor.c \ - gfilenamecompleter.c \ - gfileoutputstream.c \ - gfileiostream.c \ - gfilterinputstream.c \ - gfilteroutputstream.c \ - gicon.c \ - ginetaddress.c \ - ginetaddressmask.c \ - ginetsocketaddress.c \ - ginitable.c \ - ginputstream.c \ - gio_probes.d \ - gio_trace.h \ - gioenums.h \ - gioerror.c \ - giomodule.c \ - giomodule-priv.c \ - giomodule-priv.h \ - gioscheduler.c \ - giostream.c \ - gioprivate.h \ - giowin32-priv.h \ - gloadableicon.c \ - gmount.c \ - gmemoryinputstream.c \ - gmemoryoutputstream.c \ - gmountoperation.c \ - gnativevolumemonitor.c \ - gnativevolumemonitor.h \ - gnativesocketaddress.c \ - gnativesocketaddress.h \ - gnetworkaddress.c \ - gnetworking.c \ - gnetworkingprivate.h \ - gnetworkmonitor.c \ - gnetworkmonitorbase.c \ - gnetworkmonitorbase.h \ - gnetworkservice.c \ - goutputstream.c \ - gpermission.c \ - gpollableinputstream.c \ - gpollableoutputstream.c \ - gpollableutils.c \ - gpollfilemonitor.c \ - gpollfilemonitor.h \ - gproxy.c \ - gproxyaddress.c \ - gproxyaddressenumerator.c \ - gproxyresolver.c \ - gresolver.c \ - gresource.c \ - gresourcefile.c \ - gresourcefile.h \ - gseekable.c \ - gsimpleasyncresult.c \ - gsimpleiostream.c \ - gsimplepermission.c \ - gsocket.c \ - gsocketaddress.c \ - gsocketaddressenumerator.c \ - gsocketclient.c \ - gsocketconnectable.c \ - gsocketconnection.c \ - gsocketcontrolmessage.c \ - gsocketinputstream.c \ - gsocketinputstream.h \ - gsocketlistener.c \ - gsocketoutputstream.c \ - gsocketoutputstream.h \ - gsubprocesslauncher.c \ - gsubprocess.c \ - gsubprocesslauncher-private.h \ - gsocketservice.c \ - gsrvtarget.c \ - gsimpleproxyresolver.c \ - gtask.c \ - gtcpconnection.c \ - gtcpwrapperconnection.c \ - gthreadedsocketservice.c\ - gthemedicon.c \ - gthreadedresolver.c \ - gthreadedresolver.h \ - gtlsbackend.c \ - gtlscertificate.c \ - gtlsclientconnection.c \ - gtlsconnection.c \ - gtlsdatabase.c \ - gtlsfiledatabase.c \ - gtlsinteraction.c \ - gtlspassword.c \ - gtlsserverconnection.c \ - gdtlsconnection.c \ - gdtlsclientconnection.c \ - gdtlsserverconnection.c \ - gunionvolumemonitor.c \ - gunionvolumemonitor.h \ - gvfs.c \ - gvolume.c \ - gvolumemonitor.c \ - gzlibcompressor.c \ - gzlibdecompressor.c \ - gmountprivate.h \ - glistmodel.c \ - gliststore.c \ - $(application_sources) \ - $(gdbus_sources) \ - $(local_sources) \ - $(NULL) -nodist_gio_base_sources = \ - gioenumtypes.h \ - gioenumtypes.c \ - $(NULL) - -libgio_2_0_la_SOURCES = \ - $(gio_base_sources) \ - $(appinfo_sources) \ - $(contenttype_sources) \ - $(unix_sources) \ - $(win32_sources) \ - $(settings_sources) \ - $(NULL) -nodist_libgio_2_0_la_SOURCES = \ - $(nodist_gio_base_sources) \ - $(nodist_unix_sources) \ - $(nodist_win32_sources) \ - $(NULL) - -EXTRA_DIST += strinfo.c - -libgio_2_0_la_LIBADD = \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(platform_libadd) \ - $(ZLIB_LIBS) \ - $(SELINUX_LIBS) \ - $(GLIB_LIBS) \ - $(XATTR_LIBS) \ - $(NETWORK_LIBS) \ - $(NULL) - -libgio_2_0_la_CPPFLAGS = $(ZLIB_CFLAGS) $(AM_CPPFLAGS) - -if PLATFORM_WIN32 -no_undefined = -no-undefined -endif - -if OS_WIN32_AND_DLL_COMPILATION -gio_win32_res = gio-win32-res.o -gio_win32_res_ldflag = -Wl,$(gio_win32_res) -endif - -install-data-local: install-ms-lib - $(mkinstalldirs) $(DESTDIR)$(GIO_MODULE_DIR) - -uninstall-local: uninstall-ms-lib - -libgio_2_0_la_CFLAGS = $(AM_CFLAGS) $(GLIB_HIDDEN_VISIBILITY_CFLAGS) -libgio_2_0_la_LDFLAGS = $(GLIB_LINK_FLAGS) \ - $(gio_win32_res_ldflag) \ - -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ - -export-dynamic $(no_undefined) - -# We cannot build .m files because it would require adding AC_PROG_OBJC in -# configure.ac, and it cannot be added conditionally. That means we would always -# depend on an objective-c compiler even when not needed. To work around that -# limitation we rename .m files to .c and compile them separately with -# -xobjective-c into an intermediary library. Note that we cannot rename those -# files to .c directly in the source tree because Meson needs the .m extension. -# This must be done outside of "if OS_COCOA" block otherwise distcheck fails. -# See https://bugzilla.gnome.org/show_bug.cgi?id=672777. -OBJC_FILES = \ - gnextstepsettingsbackend.m \ - gosxcontenttype.m \ - gosxappinfo.m \ - gcocoanotificationbackend.m \ - $(NULL) -OBJC_C_FILES = $(OBJC_FILES:.m=.c) -BUILT_SOURCES += $(OBJC_C_FILES) -CLEANFILES += $(OBJC_C_FILES) -EXTRA_DIST += $(OBJC_FILES) -$(OBJC_C_FILES): %.c: %.m Makefile - cp $< $@ - -if OS_COCOA -libgio_objc_2_0_la_CFLAGS = $(libgio_2_0_la_CFLAGS) -xobjective-c -libgio_objc_2_0_la_CPPFLAGS = $(libgio_2_0_la_CPPFLAGS) -libgio_objc_2_0_la_LDFLAGS = $(libgio_2_0_la_LDFLAGS) -Wl,-framework,Foundation -Wl,-framework,AppKit -libgio_objc_2_0_la_SOURCES = \ - gnextstepsettingsbackend.c \ - gosxcontenttype.c \ - gosxappinfo.c \ - gosxappinfo.h -if MAC_OS_X_9 -libgio_objc_2_0_la_SOURCES += \ - gcocoanotificationbackend.c -endif - -noinst_LTLIBRARIES += libgio-objc-2.0.la -libgio_2_0_la_LIBADD += libgio-objc-2.0.la -endif - -if HAVE_LIBMOUNT -libgio_2_0_la_CFLAGS += $(LIBMOUNT_CFLAGS) -libgio_2_0_la_LIBADD += $(LIBMOUNT_LIBS) -endif - -EXTRA_libgio_2_0_la_DEPENDENCIES = $(gio_win32_res) $(gio_def) $(platform_deps) - -gio-win32-res.o: gio.rc - $(WINDRES) gio.rc $@ - -gio_headers = \ - gappinfo.h \ - gasyncinitable.h \ - gasyncresult.h \ - gbufferedinputstream.h \ - gbufferedoutputstream.h \ - gbytesicon.h \ - gcancellable.h \ - gcontenttype.h \ - gcharsetconverter.h \ - gconverter.h \ - gconverterinputstream.h \ - gconverteroutputstream.h \ - gdatagrambased.h \ - gdatainputstream.h \ - gdataoutputstream.h \ - gdrive.h \ - gemblem.h \ - gemblemedicon.h \ - gfile.h \ - gfileattribute.h \ - gfileenumerator.h \ - gfileicon.h \ - gfileinfo.h \ - gfileinputstream.h \ - gfilemonitor.h \ - gfilenamecompleter.h \ - gfileoutputstream.h \ - gfileiostream.h \ - gfilterinputstream.h \ - gfilteroutputstream.h \ - gicon.h \ - ginetaddress.h \ - ginetaddressmask.h \ - ginetsocketaddress.h \ - ginputstream.h \ - ginitable.h \ - gio.h \ - gio-autocleanups.h \ - giotypes.h \ - gioenums.h \ - gioerror.h \ - giomodule.h \ - gioscheduler.h \ - giostream.h \ - gloadableicon.h \ - gmount.h \ - gmemoryinputstream.h \ - gmemoryoutputstream.h \ - gmountoperation.h \ - gnativevolumemonitor.h \ - gnetworkaddress.h \ - gnetworkmonitor.h \ - gnetworkservice.h \ - goutputstream.h \ - gpermission.h \ - gpollableinputstream.h \ - gpollableoutputstream.h \ - gpollableutils.h \ - gproxyaddress.h \ - gproxy.h \ - gproxyaddressenumerator.h \ - gproxyresolver.h \ - gresolver.h \ - gresource.h \ - gseekable.h \ - gsimpleasyncresult.h \ - gsimpleiostream.h \ - gsimplepermission.h \ - gsocket.h \ - gsocketaddress.h \ - gsocketaddressenumerator.h \ - gsocketclient.h \ - gsocketconnectable.h \ - gsocketconnection.h \ - gsocketcontrolmessage.h \ - gsocketlistener.h \ - gsocketservice.h \ - gsrvtarget.h \ - gsimpleproxyresolver.h \ - gtask.h \ - gsubprocess.h \ - gsubprocesslauncher.h \ - gtcpconnection.h \ - gtcpwrapperconnection.h \ - gthreadedsocketservice.h\ - gthemedicon.h \ - gtlsbackend.h \ - gtlscertificate.h \ - gtlsclientconnection.h \ - gtlsconnection.h \ - gtlsdatabase.h \ - gtlsfiledatabase.h \ - gtlsinteraction.h \ - gtlspassword.h \ - gtlsserverconnection.h \ - gdtlsconnection.h \ - gdtlsclientconnection.h \ - gdtlsserverconnection.h \ - gvfs.h \ - gvolume.h \ - gvolumemonitor.h \ - gzlibcompressor.h \ - gzlibdecompressor.h \ - glistmodel.h \ - gliststore.h \ - $(application_headers) \ - $(settings_headers) \ - $(gdbus_headers) \ - $(NULL) - -gioincludedir=$(includedir)/glib-2.0/gio/ -gioinclude_HEADERS = \ - $(gio_headers) \ - $(NULL) -nodist_gioinclude_HEADERS = \ - gioenumtypes.h \ - gnetworking.h \ - $(NULL) - -# these sources (also mentioned above) are generated. -BUILT_SOURCES += \ - gconstructor_as_data.h \ - gioenumtypes.h \ - gioenumtypes.c \ - gdbus-daemon-generated.c \ - gdbus-daemon-generated.h \ - gnetworking.h \ - $(NULL) - -EXTRA_DIST += \ - data-to-c.py \ - gioenumtypes.h.template \ - gioenumtypes.c.template \ - gio.rc.in \ - gschema.dtd \ - $(NULL) - -BUILT_EXTRA_DIST += \ - gio.rc - -# This is read by gobject-introspection/misc/ and gtk-doc -gio-public-headers.txt: Makefile - $(AM_V_GEN) echo $(gioinclude_HEADERS) $(giowin32include_HEADERS) $(giounixinclude_HEADERS) > $@.tmp && mv $@.tmp $@ - -CLEANFILES += gdbus-daemon-generated.c gdbus-daemon-generated.h gio-public-headers.txt gconstructor_as_data.h - - -DISTCLEANFILES += \ - gioenumtypes.h \ - gioenumtypes.c - -all-local: gio-public-headers.txt - -gioenumtypes.h: $(gio_headers) gioenumtypes.h.template - $(AM_V_GEN) $(top_builddir)/gobject/glib-mkenums --template $(filter %.template,$^) $(filter-out %.template,$^) > \ - gioenumtypes.h.tmp && mv gioenumtypes.h.tmp gioenumtypes.h - -gioenumtypes.c: $(gio_headers) gioenumtypes.c.template - $(AM_V_GEN) $(top_builddir)/gobject/glib-mkenums --template $(filter %.template,$^) $(filter-out %.template,$^) > \ - gioenumtypes.c.tmp && mv gioenumtypes.c.tmp gioenumtypes.c - -gio.def: libgio-2.0.la - $(AM_V_GEN) dumpbin.exe -exports .libs/libgio-2.0-0.dll | awk 'BEGIN { print "EXPORTS" } / +[[:digit:]]+ +[[:xdigit:]]+ +[[:xdigit:]]+/{ print $$4 }' > gio.def.tmp && mv gio.def.tmp gio.def - -gio-2.0.lib: libgio-2.0.la gio.def - $(AM_V_GEN) lib.exe -machine:@LIB_EXE_MACHINE_FLAG@ -name:libgio-2.0-$(LT_CURRENT_MINUS_AGE).dll -def:$(builddir)/gio.def -out:$@ - -bin_PROGRAMS = gio-querymodules glib-compile-schemas glib-compile-resources gsettings gio-launch-desktop - -glib_compile_resources_LDADD = libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(NULL) - -glib_compile_resources_SOURCES = \ - gvdb/gvdb-format.h \ - gvdb/gvdb-builder.h \ - gvdb/gvdb-builder.c \ - glib-compile-resources.c - -gio_querymodules_SOURCES = gio-querymodules.c giomodule-priv.c -gio_querymodules_LDADD = libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(NULL) - -gio_launch_desktop_SOURCES = gio-launch-desktop.c - -gconstructor_as_data.h: $(top_srcdir)/glib/gconstructor.h data-to-c.py - $(AM_V_GEN) $(PYTHON) $(srcdir)/data-to-c.py $(top_srcdir)/glib/gconstructor.h gconstructor_code $@ - -glib_compile_schemas_LDADD = $(top_builddir)/glib/libglib-2.0.la -glib_compile_schemas_SOURCES = \ - gvdb/gvdb-format.h \ - gvdb/gvdb-builder.h \ - gvdb/gvdb-builder.c \ - glib-compile-schemas.c -nodist_glib_compile_schemas_SOURCES = \ - gconstructor_as_data.h \ - $(NULL) - -gsettings_LDADD = libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(NULL) -gsettings_SOURCES = gsettings-tool.c - -schemadir = $(datadir)/glib-2.0/schemas -dist_schema_DATA = gschema.dtd - -itsdir = $(datadir)/gettext/its -dist_its_DATA = gschema.loc gschema.its - -# ------------------------------------------------------------------------ -# SystemTap and dtrace - -if ENABLE_DTRACE -gio_probes.h: gio_probes.d - $(AM_V_GEN) $(DTRACE) -C -h -s $< -o $@.tmp - @$(SED) \ - -e "s,define STAP_HAS_SEMAPHORES 1,undef STAP_HAS_SEMAPHORES," \ - -e "s,define _SDT_HAS_SEMAPHORES 1,undef _SDT_HAS_SEMAPHORES," \ - < $@.tmp > $@ && rm -f $@.tmp - -gio_probes.lo: gio_probes.d - $(AM_V_GEN) $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -G -s $< -o $@ - -BUILT_SOURCES += gio_probes.h gio_probes.lo -CLEANFILES += gio_probes.h gio_probes.h.tmp -libgio_2_0_la_LIBADD += gio_probes.lo -endif - -tapsetdir = @ABS_TAPSET_DIR@ -EXTRA_DIST += gio.stp.in - -if ENABLE_SYSTEMTAP -tapset_DATA = libgio-2.0.so.0.@LT_CURRENT@.@LT_REVISION@.stp -CLEANFILES += $(tapset_DATA) - -$(tapset_DATA): gio.stp.in Makefile - $(AM_V_GEN)$(SED) \ - -e 's|[@]ABS_GLIB_RUNTIME_LIBDIR[@]|$(ABS_GLIB_RUNTIME_LIBDIR)|g' \ - -e 's|[@]LT_CURRENT[@]|$(LT_CURRENT)|g' \ - -e 's|[@]LT_REVISION[@]|$(LT_REVISION)|g' \ - $< > $@ -endif - -# ------------------------------------------------------------------------ -# gdbus(1) tool - -bin_PROGRAMS += gdbus -gdbus_SOURCES = gdbus-tool.c -gdbus_LDADD = libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(NULL) - -if OS_UNIX -if !OS_COCOA -# ------------------------------------------------------------------------ -# gapplication(1) tool -bin_PROGRAMS += gapplication -gapplication_SOURCES = gapplication-tool.c -gapplication_LDADD = libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(NULL) -endif -endif - -completiondir = $(datadir)/bash-completion/completions -completion_DATA = \ - completion/gapplication \ - completion/gdbus \ - completion/gio \ - completion/gsettings \ - completion/gresource -EXTRA_DIST += $(completion_DATA) - -# ------------------------------------------------------------------------ -# gresource tool - -bin_PROGRAMS += gresource -gresource_SOURCES = gresource-tool.c -gresource_CPPFLAGS = $(LIBELF_CFLAGS) $(AM_CPPFLAGS) -gresource_LDADD = libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(LIBELF_LIBS) - -# ------------------------------------------------------------------------ -# gio tool - -bin_PROGRAMS += gio -gio_SOURCES = \ - gio-tool.c \ - gio-tool.h \ - gio-tool-cat.c \ - gio-tool-copy.c \ - gio-tool-info.c \ - gio-tool-list.c \ - gio-tool-mime.c \ - gio-tool-mkdir.c \ - gio-tool-monitor.c \ - gio-tool-mount.c \ - gio-tool-move.c \ - gio-tool-open.c \ - gio-tool-rename.c \ - gio-tool-remove.c \ - gio-tool-save.c \ - gio-tool-set.c \ - gio-tool-trash.c \ - gio-tool-tree.c \ - $(NULL) -gio_LDADD = libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(NULL) - -dist-hook: $(BUILT_EXTRA_DIST) - files='$(BUILT_EXTRA_DIST)'; \ - for f in $$files; do \ - if test -f $$f; then d=.; else d=$(srcdir); fi; \ - cp $$d/$$f $(distdir) || exit 1; done - -if HAVE_GLIB_RUNTIME_LIBDIR -ABS_GLIB_RUNTIME_LIBDIR = $(realpath $(libdir)/$(GLIB_RUNTIME_LIBDIR)) -else -ABS_GLIB_RUNTIME_LIBDIR = $(libdir) -endif - -if HAVE_GLIB_RUNTIME_LIBDIR -install-data-hook: - mkdir -p $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR) - mv $(DESTDIR)$(libdir)/libgio-2.0.so.0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR) - mv $(DESTDIR)$(libdir)/libgio-2.0.so.0.$(LT_CURRENT).$(LT_REVISION) $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR) - rm -f $(DESTDIR)$(libdir)/libgio-2.0.so - ln -s $(GLIB_RUNTIME_LIBDIR)/libgio-2.0.so.0.$(LT_CURRENT).$(LT_REVISION) $(DESTDIR)$(libdir)/libgio-2.0.so -endif diff --git a/gio/fam/Makefile.am b/gio/fam/Makefile.am deleted file mode 100644 index d064943a3..000000000 --- a/gio/fam/Makefile.am +++ /dev/null @@ -1,39 +0,0 @@ -include $(top_srcdir)/glib.mk - -module_flags = -export_dynamic -avoid-version -module -no-undefined -export-symbols-regex '^g_io_module_(load|unload|query)' - -giomodule_LTLIBRARIES = libgiofam.la -giomoduledir = $(GIO_MODULE_DIR) - -libgiofam_la_SOURCES = gfamfilemonitor.c - -libgiofam_la_CFLAGS = \ - -DG_LOG_DOMAIN=\"GLib-GIO\" \ - $(gio_INCLUDES) \ - $(GLIB_DEBUG_FLAGS) \ - -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \ - -DGIO_COMPILATION \ - -DG_DISABLE_DEPRECATED - -libgiofam_la_LDFLAGS = $(module_flags) -libgiofam_la_LIBADD = \ - $(top_builddir)/gio/libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(GLIB_LIBS) \ - $(FAM_LIBS) \ - $(NULL) - -if CROSS_COMPILING -RUN_QUERY_MODULES=false -else -RUN_QUERY_MODULES=true -endif - -install-data-hook: - if $(RUN_QUERY_MODULES) && test -z "$(DESTDIR)" ; then \ - $(top_builddir)/gio/gio-querymodules$(EXEEXT) $(DESTDIR)$(GIO_MODULE_DIR) ; \ - fi - -uninstall-local: - $(RM) $(DESTDIR)$(GIO_MODULE_DIR)/giomodule.cache diff --git a/gio/gdbus-2.0/codegen/Makefile.am b/gio/gdbus-2.0/codegen/Makefile.am deleted file mode 100644 index b4e500cb0..000000000 --- a/gio/gdbus-2.0/codegen/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -include $(top_srcdir)/glib.mk - -bin_SCRIPTS = - -codegendir = $(datadir)/glib-2.0/codegen -codegen_PYTHON = \ - __init__.py \ - codegen.py \ - codegen_main.py \ - codegen_docbook.py \ - config.py \ - dbustypes.py \ - parser.py \ - utils.py \ - $(NULL) - -CLEANFILES += config.pyc - -bin_SCRIPTS += gdbus-codegen -CLEANFILES += gdbus-codegen -EXTRA_DIST += gdbus-codegen.in - -gdbus-codegen: gdbus-codegen.in Makefile $(codegen_PYTHON) - $(AM_V_GEN) sed -e 's,@DATADIR\@,$(datadir),' -e 's,@PYTHON\@,$(PYTHON),' $< > $@.tmp && mv $@.tmp $@ - @chmod a+x $@ - -clean-local: - rm -f *~ diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index b5f8b6509..c2a04ae12 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -1817,6 +1817,7 @@ _g_dbus_worker_flush_sync (GDBusWorker *worker, #define G_DBUS_DEBUG_RETURN (1<<7) #define G_DBUS_DEBUG_EMISSION (1<<8) #define G_DBUS_DEBUG_ADDRESS (1<<9) +#define G_DBUS_DEBUG_PROXY (1<<10) static gint _gdbus_debug_flags = 0; @@ -1890,6 +1891,13 @@ _g_dbus_debug_address (void) return (_gdbus_debug_flags & G_DBUS_DEBUG_ADDRESS) != 0; } +gboolean +_g_dbus_debug_proxy (void) +{ + _g_dbus_initialize (); + return (_gdbus_debug_flags & G_DBUS_DEBUG_PROXY) != 0; +} + G_LOCK_DEFINE_STATIC (print_lock); void @@ -1938,7 +1946,8 @@ _g_dbus_initialize (void) { "incoming", G_DBUS_DEBUG_INCOMING }, { "return", G_DBUS_DEBUG_RETURN }, { "emission", G_DBUS_DEBUG_EMISSION }, - { "address", G_DBUS_DEBUG_ADDRESS } + { "address", G_DBUS_DEBUG_ADDRESS }, + { "proxy", G_DBUS_DEBUG_PROXY } }; _gdbus_debug_flags = g_parse_debug_string (debug, keys, G_N_ELEMENTS (keys)); diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h index 0d85c1d85..a319166ee 100644 --- a/gio/gdbusprivate.h +++ b/gio/gdbusprivate.h @@ -91,6 +91,7 @@ gboolean _g_dbus_debug_incoming (void); gboolean _g_dbus_debug_return (void); gboolean _g_dbus_debug_emission (void); gboolean _g_dbus_debug_address (void); +gboolean _g_dbus_debug_proxy (void); void _g_dbus_debug_print_lock (void); void _g_dbus_debug_print_unlock (void); diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 6a1be4220..0ab117854 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -1235,11 +1235,14 @@ on_name_owner_changed_get_all_cb (GDBusConnection *connection, * * Either way, apps can know about this by using * get_cached_property_names() or get_cached_property(). - * - * TODO: handle G_DBUS_DEBUG flag 'proxy' and, if enabled, log the - * fact that GetAll() failed */ - //g_debug ("error: %d %d %s", error->domain, error->code, error->message); + if (G_UNLIKELY (_g_dbus_debug_proxy ())) + { + g_debug ("error: %d %d %s", + error->domain, + error->code, + error->message); + } g_error_free (error); } @@ -1432,11 +1435,14 @@ async_init_get_all_cb (GDBusConnection *connection, * * Either way, apps can know about this by using * get_cached_property_names() or get_cached_property(). - * - * TODO: handle G_DBUS_DEBUG flag 'proxy' and, if enabled, log the - * fact that GetAll() failed */ - //g_debug ("error: %d %d %s", error->domain, error->code, error->message); + if (G_UNLIKELY (_g_dbus_debug_proxy ())) + { + g_debug ("error: %d %d %s", + error->domain, + error->code, + error->message); + } g_error_free (error); } diff --git a/gio/gdtlsconnection.c b/gio/gdtlsconnection.c index 254537198..cbc25d4f2 100644 --- a/gio/gdtlsconnection.c +++ b/gio/gdtlsconnection.c @@ -295,8 +295,8 @@ g_dtls_connection_default_init (GDtlsConnectionInterface *iface) * let the user decide whether or not to accept the certificate, you * would have to return %FALSE from the signal handler on the first * attempt, and then after the connection attempt returns a - * %G_TLS_ERROR_HANDSHAKE, you can interact with the user, and if - * the user decides to accept the certificate, remember that fact, + * %G_TLS_ERROR_BAD_CERTIFICATE, you can interact with the user, and + * if the user decides to accept the certificate, remember that fact, * create a new connection, and return %TRUE from the signal handler * the next time. * @@ -1050,7 +1050,7 @@ g_dtls_connection_set_advertised_protocols (GDtlsConnection *conn, if (iface->set_advertised_protocols == NULL) return; - return iface->set_advertised_protocols (conn, protocols); + iface->set_advertised_protocols (conn, protocols); } /** diff --git a/gio/gfileinfo.c b/gio/gfileinfo.c index fa53a9c49..420e46d56 100644 --- a/gio/gfileinfo.c +++ b/gio/gfileinfo.c @@ -1180,7 +1180,8 @@ _g_file_info_set_attribute_stringv_by_id (GFileInfo *info, * g_file_info_set_attribute_stringv: * @info: a #GFileInfo. * @attribute: a file attribute key - * @attr_value: (array) (element-type utf8): a %NULL terminated array of UTF-8 strings. + * @attr_value: (array zero-terminated=1) (element-type utf8): a %NULL + * terminated array of UTF-8 strings. * * Sets the @attribute to contain the given @attr_value, * if possible. diff --git a/gio/gioenums.h b/gio/gioenums.h index d6d1e59f1..9c7d9b6ac 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -512,6 +512,9 @@ typedef enum { * ]| * but should instead treat all unrecognized error codes the same as * #G_IO_ERROR_FAILED. + * + * See also #GPollableReturn for a cheaper way of returning + * %G_IO_ERROR_WOULD_BLOCK to callers without allocating a #GError. **/ typedef enum { G_IO_ERROR_FAILED, @@ -1505,7 +1508,8 @@ typedef enum * GTlsError: * @G_TLS_ERROR_UNAVAILABLE: No TLS provider is available * @G_TLS_ERROR_MISC: Miscellaneous TLS error - * @G_TLS_ERROR_BAD_CERTIFICATE: A certificate could not be parsed + * @G_TLS_ERROR_BAD_CERTIFICATE: The certificate presented could not + * be parsed or failed validation. * @G_TLS_ERROR_NOT_TLS: The TLS handshake failed because the * peer does not seem to be a TLS server. * @G_TLS_ERROR_HANDSHAKE: The TLS handshake failed because the @@ -1921,6 +1925,30 @@ typedef enum { G_NETWORK_CONNECTIVITY_FULL = 4 } GNetworkConnectivity; +/** + * GPollableReturn: + * @G_POLLABLE_RETURN_FAILED: Generic error condition for when an operation fails. + * @G_POLLABLE_RETURN_OK: The operation was successfully finished. + * @G_POLLABLE_RETURN_WOULD_BLOCK: The operation would block. + * + * Return value for various IO operations that signal errors via the + * return value and not necessarily via a #GError. + * + * This enum exists to be able to return errors to callers without having to + * allocate a #GError. Allocating #GErrors can be quite expensive for + * regularly happening errors like %G_IO_ERROR_WOULD_BLOCK. + * + * In case of %G_POLLABLE_RETURN_FAILED a #GError should be set for the + * operation to give details about the error that happened. + * + * Since: 2.60 + */ +typedef enum { + G_POLLABLE_RETURN_FAILED = 0, + G_POLLABLE_RETURN_OK = 1, + G_POLLABLE_RETURN_WOULD_BLOCK = -G_IO_ERROR_WOULD_BLOCK +} GPollableReturn; + G_END_DECLS #endif /* __GIO_ENUMS_H__ */ diff --git a/gio/giomodule.c b/gio/giomodule.c index 36c0cefed..b92162dcc 100644 --- a/gio/giomodule.c +++ b/gio/giomodule.c @@ -700,6 +700,35 @@ try_class (GIOExtension *extension, return NULL; } +static void +print_help (const char *envvar, + GIOExtensionPoint *ep) +{ + g_print ("Supported arguments for %s environment variable:\n", envvar); + + if (g_io_extension_point_get_extensions (ep) == NULL) + g_print (" (none)\n"); + else + { + GList *l; + GIOExtension *extension; + int width = 0; + + for (l = g_io_extension_point_get_extensions (ep); l; l = l->next) + { + extension = l->data; + width = MAX (width, strlen (g_io_extension_get_name (extension))); + } + + for (l = g_io_extension_point_get_extensions (ep); l; l = l->next) + { + extension = l->data; + + g_print (" %*s - %d\n", width, g_io_extension_get_name (extension), g_io_extension_get_priority (extension)); + } + } +} + /** * _g_io_module_get_default_type: * @extension_point: the name of an extension point @@ -766,6 +795,12 @@ _g_io_module_get_default_type (const gchar *extension_point, } use_this = envvar ? g_getenv (envvar) : NULL; + if (g_strcmp0 (use_this, "help") == 0) + { + print_help (envvar, ep); + use_this = NULL; + } + if (use_this) { preferred = g_io_extension_point_get_extension_by_name (ep, use_this); @@ -874,7 +909,7 @@ _g_io_module_get_default (const gchar *extension_point, const char *use_this; GList *l; GIOExtensionPoint *ep; - GIOExtension *extension, *preferred; + GIOExtension *extension = NULL, *preferred; gpointer impl; g_rec_mutex_lock (&default_modules_lock); @@ -885,6 +920,8 @@ _g_io_module_get_default (const gchar *extension_point, if (g_hash_table_lookup_extended (default_modules, extension_point, &key, &impl)) { + /* Don’t debug here, since we’re returning a cached object which was + * already printed earlier. */ g_rec_mutex_unlock (&default_modules_lock); return impl; } @@ -899,23 +936,32 @@ _g_io_module_get_default (const gchar *extension_point, if (!ep) { + g_debug ("%s: Failed to find extension point ‘%s’", + G_STRFUNC, extension_point); g_warn_if_reached (); g_rec_mutex_unlock (&default_modules_lock); return NULL; } use_this = envvar ? g_getenv (envvar) : NULL; + if (g_strcmp0 (use_this, "help") == 0) + { + print_help (envvar, ep); + use_this = NULL; + } + if (use_this) { preferred = g_io_extension_point_get_extension_by_name (ep, use_this); if (preferred) { impl = try_implementation (extension_point, preferred, verify_func); + extension = preferred; if (impl) goto done; } else - g_warning ("Can't find module '%s' specified in %s", use_this, envvar); + g_warning ("Can't find module '%s' specified in %s", use_this, envvar); } else preferred = NULL; @@ -939,6 +985,17 @@ _g_io_module_get_default (const gchar *extension_point, impl ? g_object_ref (impl) : NULL); g_rec_mutex_unlock (&default_modules_lock); + if (impl != NULL) + { + g_assert (extension != NULL); + g_debug ("%s: Found default implementation %s (%s) for ‘%s’", + G_STRFUNC, g_io_extension_get_name (extension), + G_OBJECT_TYPE_NAME (impl), extension_point); + } + else + g_debug ("%s: Failed to find default implementation for ‘%s’", + G_STRFUNC, extension_point); + return impl; } diff --git a/gio/gioprivate.h b/gio/gioprivate.h index b79192566..a917510b9 100644 --- a/gio/gioprivate.h +++ b/gio/gioprivate.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS gboolean g_input_stream_async_read_is_via_threads (GInputStream *stream); gboolean g_input_stream_async_close_is_via_threads (GInputStream *stream); gboolean g_output_stream_async_write_is_via_threads (GOutputStream *stream); +gboolean g_output_stream_async_writev_is_via_threads (GOutputStream *stream); gboolean g_output_stream_async_close_is_via_threads (GOutputStream *stream); void g_socket_connection_set_cached_remote_address (GSocketConnection *connection, diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c index a37978e83..d5796b706 100644 --- a/gio/gkeyfilesettingsbackend.c +++ b/gio/gkeyfilesettingsbackend.c @@ -21,14 +21,20 @@ #include "config.h" +#include <glib.h> +#include <glibintl.h> + #include <stdio.h> #include <string.h> #include "gfile.h" #include "gfileinfo.h" +#include "gfileenumerator.h" #include "gfilemonitor.h" #include "gsimplepermission.h" -#include "gsettingsbackend.h" +#include "gsettingsbackendinternal.h" +#include "giomodule-priv.h" +#include "gportalsupport.h" #define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ()) @@ -41,6 +47,13 @@ typedef GSettingsBackendClass GKeyfileSettingsBackendClass; +typedef enum { + PROP_FILENAME = 1, + PROP_ROOT_PATH, + PROP_ROOT_GROUP, + PROP_DEFAULTS_DIR +} GKeyfileSettingsBackendProperty; + typedef struct { GSettingsBackend parent_instance; @@ -48,6 +61,9 @@ typedef struct GKeyFile *keyfile; GPermission *permission; gboolean writable; + char *defaults_dir; + GKeyFile *system_keyfile; + GHashTable *system_locks; /* Used as a set, owning the strings it contains */ gchar *prefix; gint prefix_len; @@ -61,10 +77,18 @@ typedef struct GFileMonitor *dir_monitor; } GKeyfileSettingsBackend; -static GType g_keyfile_settings_backend_get_type (void); -G_DEFINE_TYPE (GKeyfileSettingsBackend, - g_keyfile_settings_backend, - G_TYPE_SETTINGS_BACKEND) +#ifdef G_OS_WIN32 +#define EXTENSION_PRIORITY 10 +#else +#define EXTENSION_PRIORITY (glib_should_use_portal () ? 110 : 10) +#endif + +G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend, + g_keyfile_settings_backend, + G_TYPE_SETTINGS_BACKEND, + _g_io_modules_ensure_extension_points_registered (); + g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME, + g_define_type_id, "keyfile", EXTENSION_PRIORITY)) static void compute_checksum (guint8 *digest, @@ -184,17 +208,47 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb, if (convert_path (kfsb, key, &group, &name)) { gchar *str; + gchar *sysstr; g_assert (*name); + sysstr = g_key_file_get_value (kfsb->system_keyfile, group, name, NULL); str = g_key_file_get_value (kfsb->keyfile, group, name, NULL); + if (sysstr && + (g_hash_table_contains (kfsb->system_locks, key) || + str == NULL)) + { + g_free (str); + str = g_steal_pointer (&sysstr); + } if (str) { return_value = g_variant_parse (type, str, NULL, NULL, NULL); + if (return_value == NULL && + g_variant_type_equal (type, G_VARIANT_TYPE_STRING) && + str[0] != '\"') + { + GString *s = g_string_sized_new (strlen (str) + 2); + char *p = str; + + g_string_append_c (s, '\"'); + while (*p) + { + if (*p == '\"') + g_string_append_c (s, '\\'); + g_string_append_c (s, *p); + p++; + } + g_string_append_c (s, '\"'); + return_value = g_variant_parse (type, s->str, NULL, NULL, NULL); + g_string_free (s, TRUE); + } g_free (str); } + g_free (sysstr); + g_free (group); g_free (name); } @@ -209,6 +263,9 @@ set_to_keyfile (GKeyfileSettingsBackend *kfsb, { gchar *group, *name; + if (g_hash_table_contains (kfsb->system_locks, key)) + return FALSE; + if (convert_path (kfsb, key, &group, &name)) { if (value) @@ -287,7 +344,8 @@ g_keyfile_settings_backend_check_one (gpointer key, { WriteManyData *data = user_data; - return data->failed = !path_is_valid (data->kfsb, key); + return data->failed = g_hash_table_contains (data->kfsb->system_locks, key) || + !path_is_valid (data->kfsb, key); } static gboolean @@ -355,7 +413,9 @@ g_keyfile_settings_backend_get_writable (GSettingsBackend *backend, { GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend); - return kfsb->writable && path_is_valid (kfsb, name); + return kfsb->writable && + !g_hash_table_contains (kfsb->system_locks, name) && + path_is_valid (kfsb, name); } static GPermission * @@ -501,6 +561,9 @@ g_keyfile_settings_backend_finalize (GObject *object) g_key_file_free (kfsb->keyfile); g_object_unref (kfsb->permission); + g_key_file_unref (kfsb->system_keyfile); + g_hash_table_unref (kfsb->system_locks); + g_free (kfsb->defaults_dir); g_file_monitor_cancel (kfsb->file_monitor); g_object_unref (kfsb->file_monitor); @@ -523,25 +586,6 @@ g_keyfile_settings_backend_init (GKeyfileSettingsBackend *kfsb) } static void -g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - - object_class->finalize = g_keyfile_settings_backend_finalize; - - class->read = g_keyfile_settings_backend_read; - class->write = g_keyfile_settings_backend_write; - class->write_tree = g_keyfile_settings_backend_write_tree; - class->reset = g_keyfile_settings_backend_reset; - class->get_writable = g_keyfile_settings_backend_get_writable; - class->get_permission = g_keyfile_settings_backend_get_permission; - /* No need to implement subscribed/unsubscribe: the only point would be to - * stop monitoring the file when there's no GSettings anymore, which is no - * big win. - */ -} - -static void file_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, @@ -567,6 +611,282 @@ dir_changed (GFileMonitor *monitor, g_keyfile_settings_backend_keyfile_writable (kfsb); } +static void +load_system_settings (GKeyfileSettingsBackend *kfsb) +{ + GError *error = NULL; + const char *dir = "/etc/glib-2.0/settings"; + char *path; + char *contents; + + kfsb->system_keyfile = g_key_file_new (); + kfsb->system_locks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + if (kfsb->defaults_dir) + dir = kfsb->defaults_dir; + + path = g_build_filename (dir, "defaults", NULL); + + /* The defaults are in the same keyfile format that we use for the settings. + * It can be produced from a dconf database using: dconf dump + */ + if (!g_key_file_load_from_file (kfsb->system_keyfile, path, G_KEY_FILE_NONE, &error)) + { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + g_warning ("Failed to read %s: %s", path, error->message); + g_clear_error (&error); + } + else + g_debug ("Loading default settings from %s", path); + + g_free (path); + + path = g_build_filename (dir, "locks", NULL); + + /* The locks file is a text file containing a list paths to lock, one per line. + * It can be produced from a dconf database using: dconf list-locks + */ + if (!g_file_get_contents (path, &contents, NULL, &error)) + { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + g_warning ("Failed to read %s: %s", path, error->message); + g_clear_error (&error); + } + else + { + char **lines; + gsize i; + + g_debug ("Loading locks from %s", path); + + lines = g_strsplit (contents, "\n", 0); + for (i = 0; lines[i]; i++) + { + char *line = lines[i]; + if (line[0] == '#' || line[0] == '\0') + { + g_free (line); + continue; + } + + g_debug ("Locking key %s", line); + g_hash_table_add (kfsb->system_locks, g_steal_pointer (&line)); + } + + g_free (lines); + } + g_free (contents); + + g_free (path); +} + +static void +g_keyfile_settings_backend_constructed (GObject *object) +{ + GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); + + if (kfsb->file == NULL) + { + char *filename = g_build_filename (g_get_user_config_dir (), + "glib-2.0", "settings", "keyfile", + NULL); + kfsb->file = g_file_new_for_path (filename); + g_free (filename); + } + + if (kfsb->prefix == NULL) + { + kfsb->prefix = g_strdup ("/"); + kfsb->prefix_len = 1; + } + + kfsb->keyfile = g_key_file_new (); + kfsb->permission = g_simple_permission_new (TRUE); + + kfsb->dir = g_file_get_parent (kfsb->file); + g_file_make_directory_with_parents (kfsb->dir, NULL, NULL); + + kfsb->file_monitor = g_file_monitor (kfsb->file, G_FILE_MONITOR_NONE, NULL, NULL); + kfsb->dir_monitor = g_file_monitor (kfsb->dir, G_FILE_MONITOR_NONE, NULL, NULL); + + compute_checksum (kfsb->digest, NULL, 0); + + g_signal_connect (kfsb->file_monitor, "changed", + G_CALLBACK (file_changed), kfsb); + g_signal_connect (kfsb->dir_monitor, "changed", + G_CALLBACK (dir_changed), kfsb); + + g_keyfile_settings_backend_keyfile_writable (kfsb); + g_keyfile_settings_backend_keyfile_reload (kfsb); + + load_system_settings (kfsb); +} + +static void +g_keyfile_settings_backend_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); + + switch ((GKeyfileSettingsBackendProperty)prop_id) + { + case PROP_FILENAME: + /* Construct only. */ + g_assert (kfsb->file == NULL); + kfsb->file = g_file_new_for_path (g_value_get_string (value)); + break; + + case PROP_ROOT_PATH: + /* Construct only. */ + g_assert (kfsb->prefix == NULL); + kfsb->prefix = g_value_dup_string (value); + if (kfsb->prefix) + kfsb->prefix_len = strlen (kfsb->prefix); + break; + + case PROP_ROOT_GROUP: + /* Construct only. */ + g_assert (kfsb->root_group == NULL); + kfsb->root_group = g_value_dup_string (value); + if (kfsb->root_group) + kfsb->root_group_len = strlen (kfsb->root_group); + break; + + case PROP_DEFAULTS_DIR: + /* Construct only. */ + g_assert (kfsb->defaults_dir == NULL); + kfsb->defaults_dir = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_keyfile_settings_backend_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); + + switch ((GKeyfileSettingsBackendProperty)prop_id) + { + case PROP_FILENAME: + g_value_set_string (value, g_file_peek_path (kfsb->file)); + break; + + case PROP_ROOT_PATH: + g_value_set_string (value, kfsb->prefix); + break; + + case PROP_ROOT_GROUP: + g_value_set_string (value, kfsb->root_group); + break; + + case PROP_DEFAULTS_DIR: + g_value_set_string (value, kfsb->defaults_dir); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = g_keyfile_settings_backend_finalize; + object_class->constructed = g_keyfile_settings_backend_constructed; + object_class->get_property = g_keyfile_settings_backend_get_property; + object_class->set_property = g_keyfile_settings_backend_set_property; + + class->read = g_keyfile_settings_backend_read; + class->write = g_keyfile_settings_backend_write; + class->write_tree = g_keyfile_settings_backend_write_tree; + class->reset = g_keyfile_settings_backend_reset; + class->get_writable = g_keyfile_settings_backend_get_writable; + class->get_permission = g_keyfile_settings_backend_get_permission; + /* No need to implement subscribed/unsubscribe: the only point would be to + * stop monitoring the file when there's no GSettings anymore, which is no + * big win. + */ + + /** + * GKeyfileSettingsBackend:filename: + * + * The location where the settings are stored on disk. + * + * Defaults to `$XDG_CONFIG_HOME/glib-2.0/settings/keyfile`. + */ + g_object_class_install_property (object_class, + PROP_FILENAME, + g_param_spec_string ("filename", + P_("Filename"), + P_("The filename"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GKeyfileSettingsBackend:root-path: + * + * All settings read to or written from the backend must fall under the + * path given in @root_path (which must start and end with a slash and + * not contain two consecutive slashes). @root_path may be "/". + * + * Defaults to "/". + */ + g_object_class_install_property (object_class, + PROP_ROOT_PATH, + g_param_spec_string ("root-path", + P_("Root path"), + P_("The root path"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GKeyfileSettingsBackend:root-group: + * + * If @root_group is non-%NULL then it specifies the name of the keyfile + * group used for keys that are written directly below the root path. + * + * Defaults to NULL. + */ + g_object_class_install_property (object_class, + PROP_ROOT_GROUP, + g_param_spec_string ("root-group", + P_("Root group"), + P_("The root group"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GKeyfileSettingsBackend:default-dir: + * + * The directory where the system defaults and locks are located. + * + * Defaults to `/etc/glib-2.0/settings`. + */ + g_object_class_install_property (object_class, + PROP_DEFAULTS_DIR, + g_param_spec_string ("defaults-dir", + P_("Default dir"), + P_("Defaults dir"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + /** * g_keyfile_settings_backend_new: * @filename: the filename of the keyfile @@ -619,6 +939,11 @@ dir_changed (GFileMonitor *monitor, * characters in your path names or '=' in your key names you may be in * trouble. * + * The backend reads default values from a keyfile called `defaults` in + * the directory specified by the #GKeyfileSettingsBackend:defaults-dir property, + * and a list of locked keys from a text file with the name `locks` in + * the same location. + * * Returns: (transfer full): a keyfile-backed #GSettingsBackend **/ GSettingsBackend * @@ -626,43 +951,15 @@ g_keyfile_settings_backend_new (const gchar *filename, const gchar *root_path, const gchar *root_group) { - GKeyfileSettingsBackend *kfsb; - g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (root_path != NULL, NULL); g_return_val_if_fail (g_str_has_prefix (root_path, "/"), NULL); g_return_val_if_fail (g_str_has_suffix (root_path, "/"), NULL); g_return_val_if_fail (strstr (root_path, "//") == NULL, NULL); - kfsb = g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND, NULL); - kfsb->keyfile = g_key_file_new (); - kfsb->permission = g_simple_permission_new (TRUE); - - kfsb->file = g_file_new_for_path (filename); - kfsb->dir = g_file_get_parent (kfsb->file); - g_file_make_directory_with_parents (kfsb->dir, NULL, NULL); - - kfsb->file_monitor = g_file_monitor (kfsb->file, 0, NULL, NULL); - kfsb->dir_monitor = g_file_monitor (kfsb->dir, 0, NULL, NULL); - - kfsb->prefix_len = strlen (root_path); - kfsb->prefix = g_strdup (root_path); - - if (root_group) - { - kfsb->root_group_len = strlen (root_group); - kfsb->root_group = g_strdup (root_group); - } - - compute_checksum (kfsb->digest, NULL, 0); - - g_signal_connect (kfsb->file_monitor, "changed", - G_CALLBACK (file_changed), kfsb); - g_signal_connect (kfsb->dir_monitor, "changed", - G_CALLBACK (dir_changed), kfsb); - - g_keyfile_settings_backend_keyfile_writable (kfsb); - g_keyfile_settings_backend_keyfile_reload (kfsb); - - return G_SETTINGS_BACKEND (kfsb); + return G_SETTINGS_BACKEND (g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND, + "filename", filename, + "root-path", root_path, + "root-group", root_group, + NULL)); } diff --git a/gio/gliststore.c b/gio/gliststore.c index ae8e2c1d8..7b2a453d6 100644 --- a/gio/gliststore.c +++ b/gio/gliststore.c @@ -55,6 +55,7 @@ struct _GListStore /* cache */ guint last_position; GSequenceIter *last_iter; + gboolean last_position_valid; }; enum @@ -79,7 +80,8 @@ g_list_store_items_changed (GListStore *store, if (position <= store->last_position) { store->last_iter = NULL; - store->last_position = -1u; + store->last_position = 0; + store->last_position_valid = FALSE; } g_list_model_items_changed (G_LIST_MODEL (store), position, removed, added); @@ -179,11 +181,11 @@ g_list_store_get_item (GListModel *list, GListStore *store = G_LIST_STORE (list); GSequenceIter *it = NULL; - if (store->last_position != -1u) + if (store->last_position_valid) { - if (store->last_position == position + 1) + if (position < G_MAXUINT && store->last_position == position + 1) it = g_sequence_iter_prev (store->last_iter); - else if (store->last_position == position - 1) + else if (position > 0 && store->last_position == position - 1) it = g_sequence_iter_next (store->last_iter); else if (store->last_position == position) it = store->last_iter; @@ -194,6 +196,7 @@ g_list_store_get_item (GListModel *list, store->last_iter = it; store->last_position = position; + store->last_position_valid = TRUE; if (g_sequence_iter_is_end (it)) return NULL; @@ -213,7 +216,8 @@ static void g_list_store_init (GListStore *store) { store->items = g_sequence_new (g_object_unref); - store->last_position = -1u; + store->last_position = 0; + store->last_position_valid = FALSE; } /** diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c index 57d2d5dfe..6d44989bf 100644 --- a/gio/glocalfileoutputstream.c +++ b/gio/glocalfileoutputstream.c @@ -38,6 +38,7 @@ #ifdef G_OS_UNIX #include <unistd.h> #include "gfiledescriptorbased.h" +#include <sys/uio.h> #endif #include "glib-private.h" @@ -93,6 +94,14 @@ static gssize g_local_file_output_stream_write (GOutputStream *s gsize count, GCancellable *cancellable, GError **error); +#ifdef G_OS_UNIX +static gboolean g_local_file_output_stream_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error); +#endif static gboolean g_local_file_output_stream_close (GOutputStream *stream, GCancellable *cancellable, GError **error); @@ -142,6 +151,9 @@ g_local_file_output_stream_class_init (GLocalFileOutputStreamClass *klass) gobject_class->finalize = g_local_file_output_stream_finalize; stream_class->write_fn = g_local_file_output_stream_write; +#ifdef G_OS_UNIX + stream_class->writev_fn = g_local_file_output_stream_writev; +#endif stream_class->close_fn = g_local_file_output_stream_close; file_stream_class->query_info = g_local_file_output_stream_query_info; file_stream_class->get_etag = g_local_file_output_stream_get_etag; @@ -203,6 +215,89 @@ g_local_file_output_stream_write (GOutputStream *stream, return res; } +/* On Windows there is no equivalent API for files. The closest API to that is + * WriteFileGather() but it is useless in general: it requires, among other + * things, that each chunk is the size of a whole page and in memory aligned + * to a page. We can't possibly guarantee that in GLib. + */ +#ifdef G_OS_UNIX +/* Macro to check if struct iovec and GOutputVector have the same ABI */ +#define G_OUTPUT_VECTOR_IS_IOVEC (sizeof (struct iovec) == sizeof (GOutputVector) && \ + sizeof ((struct iovec *) 0)->iov_base == sizeof ((GOutputVector *) 0)->buffer && \ + G_STRUCT_OFFSET (struct iovec, iov_base) == G_STRUCT_OFFSET (GOutputVector, buffer) && \ + sizeof ((struct iovec *) 0)->iov_len == sizeof((GOutputVector *) 0)->size && \ + G_STRUCT_OFFSET (struct iovec, iov_len) == G_STRUCT_OFFSET (GOutputVector, size)) + +static gboolean +g_local_file_output_stream_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error) +{ + GLocalFileOutputStream *file; + gssize res; + struct iovec *iov; + + if (bytes_written) + *bytes_written = 0; + + /* Clamp to G_MAXINT as writev() takes an integer for the number of vectors. + * We handle this like a short write in this case + */ + if (n_vectors > G_MAXINT) + n_vectors = G_MAXINT; + + file = G_LOCAL_FILE_OUTPUT_STREAM (stream); + + if (G_OUTPUT_VECTOR_IS_IOVEC) + { + /* ABI is compatible */ + iov = (struct iovec *) vectors; + } + else + { + gsize i; + + /* ABI is incompatible */ + iov = g_newa (struct iovec, n_vectors); + for (i = 0; i < n_vectors; i++) + { + iov[i].iov_base = (void *)vectors[i].buffer; + iov[i].iov_len = vectors[i].size; + } + } + + while (1) + { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + res = writev (file->priv->fd, iov, n_vectors); + if (res == -1) + { + int errsv = errno; + + if (errsv == EINTR) + continue; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error writing to file: %s"), + g_strerror (errsv)); + } + else if (bytes_written) + { + *bytes_written = res; + } + + break; + } + + return res != -1; +} +#endif + void _g_local_file_output_stream_set_do_close (GLocalFileOutputStream *out, gboolean do_close) diff --git a/gio/gnetworkaddress.c b/gio/gnetworkaddress.c index 1651f89ed..60736874e 100644 --- a/gio/gnetworkaddress.c +++ b/gio/gnetworkaddress.c @@ -1120,12 +1120,17 @@ on_address_timeout (gpointer user_data) { GNetworkAddressAddressEnumerator *addr_enum = user_data; + /* Upon completion it may get unref'd by the owner */ + g_object_ref (addr_enum); + /* If ipv6 didn't come in yet, just complete the task */ if (addr_enum->queued_task != NULL) complete_queued_task (addr_enum, g_steal_pointer (&addr_enum->queued_task), g_steal_pointer (&addr_enum->last_error)); g_clear_pointer (&addr_enum->wait_source, g_source_unref); + g_object_unref (addr_enum); + return G_SOURCE_REMOVE; } diff --git a/gio/goutputstream.c b/gio/goutputstream.c index 3e658e88a..10f9aa732 100644 --- a/gio/goutputstream.c +++ b/gio/goutputstream.c @@ -72,6 +72,23 @@ static void g_output_stream_real_write_async (GOutputStream *s static gssize g_output_stream_real_write_finish (GOutputStream *stream, GAsyncResult *result, GError **error); +static gboolean g_output_stream_real_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error); +static void g_output_stream_real_writev_async (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data); +static gboolean g_output_stream_real_writev_finish (GOutputStream *stream, + GAsyncResult *result, + gsize *bytes_written, + GError **error); static void g_output_stream_real_splice_async (GOutputStream *stream, GInputStream *source, GOutputStreamSpliceFlags flags, @@ -134,6 +151,9 @@ g_output_stream_class_init (GOutputStreamClass *klass) klass->write_async = g_output_stream_real_write_async; klass->write_finish = g_output_stream_real_write_finish; + klass->writev_fn = g_output_stream_real_writev; + klass->writev_async = g_output_stream_real_writev_async; + klass->writev_finish = g_output_stream_real_writev_finish; klass->splice_async = g_output_stream_real_splice_async; klass->splice_finish = g_output_stream_real_splice_finish; klass->flush_async = g_output_stream_real_flush_async; @@ -286,9 +306,7 @@ g_output_stream_write_all (GOutputStream *stream, *bytes_written = _bytes_written; return FALSE; } - - if (res == 0) - g_warning ("Write returned zero without error"); + g_return_val_if_fail (res > 0, FALSE); _bytes_written += res; } @@ -300,6 +318,203 @@ g_output_stream_write_all (GOutputStream *stream, } /** + * g_output_stream_writev: + * @stream: a #GOutputStream. + * @vectors: (array length=n_vectors): the buffer containing the #GOutputVectors to write. + * @n_vectors: the number of vectors to write + * @bytes_written: (out) (optional): location to store the number of bytes that were + * written to the stream + * @cancellable: (nullable): optional cancellable object + * @error: location to store the error occurring, or %NULL to ignore + * + * Tries to write the bytes contained in the @n_vectors @vectors into the + * stream. Will block during the operation. + * + * If @n_vectors is 0 or the sum of all bytes in @vectors is 0, returns 0 and + * does nothing. + * + * On success, the number of bytes written to the stream is returned. + * It is not an error if this is not the same as the requested size, as it + * can happen e.g. on a partial I/O error, or if there is not enough + * storage in the stream. All writes block until at least one byte + * is written or an error occurs; 0 is never returned (unless + * @n_vectors is 0 or the sum of all bytes in @vectors is 0). + * + * If @cancellable is not %NULL, then the operation can be cancelled by + * triggering the cancellable object from another thread. If the operation + * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an + * operation was partially finished when the operation was cancelled the + * partial result will be returned, without an error. + * + * Some implementations of g_output_stream_writev() may have limitations on the + * aggregate buffer size, and will return %G_IO_ERROR_INVALID_ARGUMENT if these + * are exceeded. For example, when writing to a local file on UNIX platforms, + * the aggregate buffer size must not exceed %G_MAXSSIZE bytes. + * + * Virtual: writev_fn + * + * Returns: %TRUE on success, %FALSE if there was an error + * + * Since: 2.60 + */ +gboolean +g_output_stream_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error) +{ + GOutputStreamClass *class; + gboolean res; + gsize _bytes_written = 0; + + if (bytes_written) + *bytes_written = 0; + + g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE); + g_return_val_if_fail (vectors != NULL || n_vectors == 0, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (n_vectors == 0) + return TRUE; + + class = G_OUTPUT_STREAM_GET_CLASS (stream); + + g_return_val_if_fail (class->writev_fn != NULL, FALSE); + + if (!g_output_stream_set_pending (stream, error)) + return FALSE; + + if (cancellable) + g_cancellable_push_current (cancellable); + + res = class->writev_fn (stream, vectors, n_vectors, &_bytes_written, cancellable, error); + + g_warn_if_fail (res || _bytes_written == 0); + g_warn_if_fail (res || (error == NULL || *error != NULL)); + + if (cancellable) + g_cancellable_pop_current (cancellable); + + g_output_stream_clear_pending (stream); + + if (bytes_written) + *bytes_written = _bytes_written; + + return res; +} + +/** + * g_output_stream_writev_all: + * @stream: a #GOutputStream. + * @vectors: (array length=n_vectors): the buffer containing the #GOutputVectors to write. + * @n_vectors: the number of vectors to write + * @bytes_written: (out) (optional): location to store the number of bytes that were + * written to the stream + * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. + * @error: location to store the error occurring, or %NULL to ignore + * + * Tries to write the bytes contained in the @n_vectors @vectors into the + * stream. Will block during the operation. + * + * This function is similar to g_output_stream_writev(), except it tries to + * write as many bytes as requested, only stopping on an error. + * + * On a successful write of all @n_vectors vectors, %TRUE is returned, and + * @bytes_written is set to the sum of all the sizes of @vectors. + * + * If there is an error during the operation %FALSE is returned and @error + * is set to indicate the error status. + * + * As a special exception to the normal conventions for functions that + * use #GError, if this function returns %FALSE (and sets @error) then + * @bytes_written will be set to the number of bytes that were + * successfully written before the error was encountered. This + * functionality is only available from C. If you need it from another + * language then you must write your own loop around + * g_output_stream_write(). + * + * The content of the individual elements of @vectors might be changed by this + * function. + * + * Returns: %TRUE on success, %FALSE if there was an error + * + * Since: 2.60 + */ +gboolean +g_output_stream_writev_all (GOutputStream *stream, + GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error) +{ + gsize _bytes_written = 0; + gsize i, to_be_written = 0; + + if (bytes_written) + *bytes_written = 0; + + g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE); + g_return_val_if_fail (vectors != NULL || n_vectors == 0, FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* We can't write more than G_MAXSIZE bytes overall, otherwise we + * would overflow the bytes_written counter */ + for (i = 0; i < n_vectors; i++) + { + if (to_be_written > G_MAXSIZE - vectors[i].size) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + _("Sum of vectors passed to %s too large"), G_STRFUNC); + return FALSE; + } + to_be_written += vectors[i].size; + } + + _bytes_written = 0; + while (n_vectors > 0 && to_be_written > 0) + { + gsize n_written = 0; + gboolean res; + + res = g_output_stream_writev (stream, vectors, n_vectors, &n_written, cancellable, error); + + if (!res) + { + if (bytes_written) + *bytes_written = _bytes_written; + return FALSE; + } + + g_return_val_if_fail (n_written > 0, FALSE); + _bytes_written += n_written; + + /* skip vectors that have been written in full */ + while (n_vectors > 0 && n_written >= vectors[0].size) + { + n_written -= vectors[0].size; + ++vectors; + --n_vectors; + } + /* skip partially written vector data */ + if (n_written > 0 && n_vectors > 0) + { + vectors[0].size -= n_written; + vectors[0].buffer = ((guint8 *) vectors[0].buffer) + n_written; + } + } + + if (bytes_written) + *bytes_written = _bytes_written; + + return TRUE; +} + +/** * g_output_stream_printf: * @stream: a #GOutputStream. * @bytes_written: (out) (optional): location to store the number of bytes that was @@ -923,7 +1138,6 @@ write_all_callback (GObject *stream, g_task_return_boolean (task, TRUE); g_object_unref (task); } - else g_output_stream_write_async (G_OUTPUT_STREAM (stream), data->buffer + data->bytes_written, @@ -1060,6 +1274,329 @@ g_output_stream_write_all_finish (GOutputStream *stream, return g_task_propagate_boolean (task, error); } +/** + * g_output_stream_writev_async: + * @stream: A #GOutputStream. + * @vectors: (array length=n_vectors): the buffer containing the #GOutputVectors to write. + * @n_vectors: the number of vectors to write + * @io_priority: the I/O priority of the request. + * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. + * @callback: (scope async): callback to call when the request is satisfied + * @user_data: (closure): the data to pass to callback function + * + * Request an asynchronous write of the bytes contained in @n_vectors @vectors into + * the stream. When the operation is finished @callback will be called. + * You can then call g_output_stream_writev_finish() to get the result of the + * operation. + * + * During an async request no other sync and async calls are allowed, + * and will result in %G_IO_ERROR_PENDING errors. + * + * On success, the number of bytes written will be passed to the + * @callback. It is not an error if this is not the same as the + * requested size, as it can happen e.g. on a partial I/O error, + * but generally we try to write as many bytes as requested. + * + * You are guaranteed that this method will never fail with + * %G_IO_ERROR_WOULD_BLOCK — if @stream can't accept more data, the + * method will just wait until this changes. + * + * Any outstanding I/O request with higher priority (lower numerical + * value) will be executed before an outstanding request with lower + * priority. Default priority is %G_PRIORITY_DEFAULT. + * + * The asynchronous methods have a default fallback that uses threads + * to implement asynchronicity, so they are optional for inheriting + * classes. However, if you override one you must override all. + * + * For the synchronous, blocking version of this function, see + * g_output_stream_writev(). + * + * Note that no copy of @vectors will be made, so it must stay valid + * until @callback is called. + * + * Since: 2.60 + */ +void +g_output_stream_writev_async (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GOutputStreamClass *class; + + g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); + g_return_if_fail (vectors != NULL || n_vectors == 0); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + class = G_OUTPUT_STREAM_GET_CLASS (stream); + g_return_if_fail (class->writev_async != NULL); + + class->writev_async (stream, vectors, n_vectors, io_priority, cancellable, + callback, user_data); +} + +/** + * g_output_stream_writev_finish: + * @stream: a #GOutputStream. + * @result: a #GAsyncResult. + * @bytes_written: (out) (optional): location to store the number of bytes that were written to the stream + * @error: a #GError location to store the error occurring, or %NULL to + * ignore. + * + * Finishes a stream writev operation. + * + * Returns: %TRUE on success, %FALSE if there was an error + * + * Since: 2.60 + */ +gboolean +g_output_stream_writev_finish (GOutputStream *stream, + GAsyncResult *result, + gsize *bytes_written, + GError **error) +{ + GOutputStreamClass *class; + gboolean res; + gsize _bytes_written = 0; + + g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + class = G_OUTPUT_STREAM_GET_CLASS (stream); + g_return_val_if_fail (class->writev_finish != NULL, FALSE); + + res = class->writev_finish (stream, result, &_bytes_written, error); + + g_warn_if_fail (res || _bytes_written == 0); + g_warn_if_fail (res || (error == NULL || *error != NULL)); + + if (bytes_written) + *bytes_written = _bytes_written; + + return res; +} + +typedef struct +{ + GOutputVector *vectors; + gsize n_vectors; /* (unowned) */ + gsize bytes_written; +} AsyncWritevAll; + +static void +free_async_writev_all (gpointer data) +{ + g_slice_free (AsyncWritevAll, data); +} + +static void +writev_all_callback (GObject *stream, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = user_data; + AsyncWritevAll *data = g_task_get_task_data (task); + gint priority = g_task_get_priority (task); + GCancellable *cancellable = g_task_get_cancellable (task); + + if (result) + { + GError *error = NULL; + gboolean res; + gsize n_written = 0; + + res = g_output_stream_writev_finish (G_OUTPUT_STREAM (stream), result, &n_written, &error); + + if (!res) + { + g_task_return_error (task, g_steal_pointer (&error)); + g_object_unref (task); + return; + } + + g_warn_if_fail (n_written > 0); + data->bytes_written += n_written; + + /* skip vectors that have been written in full */ + while (data->n_vectors > 0 && n_written >= data->vectors[0].size) + { + n_written -= data->vectors[0].size; + ++data->vectors; + --data->n_vectors; + } + /* skip partially written vector data */ + if (n_written > 0 && data->n_vectors > 0) + { + data->vectors[0].size -= n_written; + data->vectors[0].buffer = ((guint8 *) data->vectors[0].buffer) + n_written; + } + } + + if (data->n_vectors == 0) + { + g_task_return_boolean (task, TRUE); + g_object_unref (task); + } + else + g_output_stream_writev_async (G_OUTPUT_STREAM (stream), + data->vectors, + data->n_vectors, + priority, + cancellable, + writev_all_callback, g_steal_pointer (&task)); +} + +static void +writev_all_async_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GOutputStream *stream = G_OUTPUT_STREAM (source_object); + AsyncWritevAll *data = task_data; + GError *error = NULL; + + if (g_output_stream_writev_all (stream, data->vectors, data->n_vectors, &data->bytes_written, + g_task_get_cancellable (task), &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +/** + * g_output_stream_writev_all_async: + * @stream: A #GOutputStream + * @vectors: (array length=n_vectors): the buffer containing the #GOutputVectors to write. + * @n_vectors: the number of vectors to write + * @io_priority: the I/O priority of the request + * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore + * @callback: (scope async): callback to call when the request is satisfied + * @user_data: (closure): the data to pass to callback function + * + * Request an asynchronous write of the bytes contained in the @n_vectors @vectors into + * the stream. When the operation is finished @callback will be called. + * You can then call g_output_stream_writev_all_finish() to get the result of the + * operation. + * + * This is the asynchronous version of g_output_stream_writev_all(). + * + * Call g_output_stream_writev_all_finish() to collect the result. + * + * Any outstanding I/O request with higher priority (lower numerical + * value) will be executed before an outstanding request with lower + * priority. Default priority is %G_PRIORITY_DEFAULT. + * + * Note that no copy of @vectors will be made, so it must stay valid + * until @callback is called. The content of the individual elements + * of @vectors might be changed by this function. + * + * Since: 2.60 + */ +void +g_output_stream_writev_all_async (GOutputStream *stream, + GOutputVector *vectors, + gsize n_vectors, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AsyncWritevAll *data; + GTask *task; + gsize i, to_be_written = 0; + + g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); + g_return_if_fail (vectors != NULL || n_vectors == 0); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (stream, cancellable, callback, user_data); + data = g_slice_new0 (AsyncWritevAll); + data->vectors = vectors; + data->n_vectors = n_vectors; + + g_task_set_source_tag (task, g_output_stream_writev_all_async); + g_task_set_task_data (task, data, free_async_writev_all); + g_task_set_priority (task, io_priority); + + /* We can't write more than G_MAXSIZE bytes overall, otherwise we + * would overflow the bytes_written counter */ + for (i = 0; i < n_vectors; i++) + { + if (to_be_written > G_MAXSIZE - vectors[i].size) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + _("Sum of vectors passed to %s too large"), + G_STRFUNC); + g_object_unref (task); + return; + } + to_be_written += vectors[i].size; + } + + /* If async writes are going to be handled via the threadpool anyway + * then we may as well do it with a single dispatch instead of + * bouncing in and out. + */ + if (g_output_stream_async_writev_is_via_threads (stream)) + { + g_task_run_in_thread (task, writev_all_async_thread); + g_object_unref (task); + } + else + writev_all_callback (G_OBJECT (stream), NULL, g_steal_pointer (&task)); +} + +/** + * g_output_stream_writev_all_finish: + * @stream: a #GOutputStream + * @result: a #GAsyncResult + * @bytes_written: (out) (optional): location to store the number of bytes that were written to the stream + * @error: a #GError location to store the error occurring, or %NULL to ignore. + * + * Finishes an asynchronous stream write operation started with + * g_output_stream_writev_all_async(). + * + * As a special exception to the normal conventions for functions that + * use #GError, if this function returns %FALSE (and sets @error) then + * @bytes_written will be set to the number of bytes that were + * successfully written before the error was encountered. This + * functionality is only available from C. If you need it from another + * language then you must write your own loop around + * g_output_stream_writev_async(). + * + * Returns: %TRUE on success, %FALSE if there was an error + * + * Since: 2.60 + */ +gboolean +g_output_stream_writev_all_finish (GOutputStream *stream, + GAsyncResult *result, + gsize *bytes_written, + GError **error) +{ + GTask *task; + + g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE); + g_return_val_if_fail (g_task_is_valid (result, stream), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + task = G_TASK (result); + + if (bytes_written) + { + AsyncWritevAll *data = (AsyncWritevAll *)g_task_get_task_data (task); + + *bytes_written = data->bytes_written; + } + + return g_task_propagate_boolean (task, error); +} + static void write_bytes_callback (GObject *stream, GAsyncResult *result, @@ -1713,6 +2250,28 @@ g_output_stream_async_write_is_via_threads (GOutputStream *stream) } /*< internal > + * g_output_stream_async_writev_is_via_threads: + * @stream: a #GOutputStream. + * + * Checks if an output stream's writev_async function uses threads. + * + * Returns: %TRUE if @stream's writev_async function uses threads. + **/ +gboolean +g_output_stream_async_writev_is_via_threads (GOutputStream *stream) +{ + GOutputStreamClass *class; + + g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE); + + class = G_OUTPUT_STREAM_GET_CLASS (stream); + + return (class->writev_async == g_output_stream_real_writev_async && + !(G_IS_POLLABLE_OUTPUT_STREAM (stream) && + g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (stream)))); +} + +/*< internal > * g_output_stream_async_close_is_via_threads: * @stream: output stream * @@ -1733,6 +2292,69 @@ g_output_stream_async_close_is_via_threads (GOutputStream *stream) } /******************************************** + * Default implementation of sync ops * + ********************************************/ +static gboolean +g_output_stream_real_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error) +{ + GOutputStreamClass *class; + gsize _bytes_written = 0; + gsize i; + GError *err = NULL; + + class = G_OUTPUT_STREAM_GET_CLASS (stream); + + if (bytes_written) + *bytes_written = 0; + + for (i = 0; i < n_vectors; i++) + { + gssize res = 0; + + /* Would we overflow here? In that case simply return and let the caller + * handle this like a short write */ + if (_bytes_written > G_MAXSIZE - vectors[i].size) + break; + + res = class->write_fn (stream, vectors[i].buffer, vectors[i].size, cancellable, &err); + + if (res == -1) + { + /* If we already wrote something we handle this like a short write + * and assume that on the next call the same error happens again, or + * everything finishes successfully without data loss then + */ + if (_bytes_written > 0) + { + if (bytes_written) + *bytes_written = _bytes_written; + + g_clear_error (&err); + return TRUE; + } + + g_propagate_error (error, err); + return FALSE; + } + + _bytes_written += res; + /* if we had a short write break the loop here */ + if (res < vectors[i].size) + break; + } + + if (bytes_written) + *bytes_written = _bytes_written; + + return TRUE; +} + +/******************************************** * Default implementation of async ops * ********************************************/ @@ -1853,6 +2475,172 @@ g_output_stream_real_write_finish (GOutputStream *stream, } typedef struct { + const GOutputVector *vectors; + gsize n_vectors; /* (unowned) */ + gsize bytes_written; +} WritevData; + +static void +free_writev_data (WritevData *op) +{ + g_slice_free (WritevData, op); +} + +static void +writev_async_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GOutputStream *stream = source_object; + WritevData *op = task_data; + GOutputStreamClass *class; + GError *error = NULL; + gboolean res; + + class = G_OUTPUT_STREAM_GET_CLASS (stream); + res = class->writev_fn (stream, op->vectors, op->n_vectors, + &op->bytes_written, cancellable, &error); + + g_warn_if_fail (res || op->bytes_written == 0); + g_warn_if_fail (res || error != NULL); + + if (!res) + g_task_return_error (task, g_steal_pointer (&error)); + else + g_task_return_boolean (task, TRUE); +} + +static void writev_async_pollable (GPollableOutputStream *stream, + GTask *task); + +static gboolean +writev_async_pollable_ready (GPollableOutputStream *stream, + gpointer user_data) +{ + GTask *task = user_data; + + writev_async_pollable (stream, task); + return G_SOURCE_REMOVE; +} + +static void +writev_async_pollable (GPollableOutputStream *stream, + GTask *task) +{ + GError *error = NULL; + WritevData *op = g_task_get_task_data (task); + GPollableReturn res; + gsize bytes_written = 0; + + if (g_task_return_error_if_cancelled (task)) + return; + + res = G_POLLABLE_OUTPUT_STREAM_GET_INTERFACE (stream)-> + writev_nonblocking (stream, op->vectors, op->n_vectors, &bytes_written, &error); + + switch (res) + { + case G_POLLABLE_RETURN_WOULD_BLOCK: + { + GSource *source; + + g_warn_if_fail (error == NULL); + g_warn_if_fail (bytes_written == 0); + + source = g_pollable_output_stream_create_source (stream, + g_task_get_cancellable (task)); + g_task_attach_source (task, source, + (GSourceFunc) writev_async_pollable_ready); + g_source_unref (source); + } + break; + case G_POLLABLE_RETURN_OK: + g_warn_if_fail (error == NULL); + op->bytes_written = bytes_written; + g_task_return_boolean (task, TRUE); + break; + case G_POLLABLE_RETURN_FAILED: + g_warn_if_fail (bytes_written == 0); + g_warn_if_fail (error != NULL); + g_task_return_error (task, g_steal_pointer (&error)); + break; + default: + g_assert_not_reached (); + } +} + +static void +g_output_stream_real_writev_async (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + WritevData *op; + GError *error = NULL; + + op = g_slice_new0 (WritevData); + task = g_task_new (stream, cancellable, callback, user_data); + op->vectors = vectors; + op->n_vectors = n_vectors; + + g_task_set_check_cancellable (task, FALSE); + g_task_set_source_tag (task, g_output_stream_writev_async); + g_task_set_priority (task, io_priority); + g_task_set_task_data (task, op, (GDestroyNotify) free_writev_data); + + if (n_vectors == 0) + { + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + if (!g_output_stream_set_pending (stream, &error)) + { + g_task_return_error (task, g_steal_pointer (&error)); + g_object_unref (task); + return; + } + + if (!g_output_stream_async_writev_is_via_threads (stream)) + writev_async_pollable (G_POLLABLE_OUTPUT_STREAM (stream), task); + else + g_task_run_in_thread (task, writev_async_thread); + + g_object_unref (task); +} + +static gboolean +g_output_stream_real_writev_finish (GOutputStream *stream, + GAsyncResult *result, + gsize *bytes_written, + GError **error) +{ + GTask *task; + + g_return_val_if_fail (g_task_is_valid (result, stream), FALSE); + g_return_val_if_fail (g_async_result_is_tagged (result, g_output_stream_writev_async), FALSE); + + g_output_stream_clear_pending (stream); + + task = G_TASK (result); + + if (bytes_written) + { + WritevData *op = g_task_get_task_data (task); + + *bytes_written = op->bytes_written; + } + + return g_task_propagate_boolean (task, error); +} + +typedef struct { GInputStream *source; GOutputStreamSpliceFlags flags; gssize n_read; diff --git a/gio/goutputstream.h b/gio/goutputstream.h index fef1b8fdf..dc0f4925a 100644 --- a/gio/goutputstream.h +++ b/gio/goutputstream.h @@ -119,11 +119,28 @@ struct _GOutputStreamClass GAsyncResult *result, GError **error); + gboolean (* writev_fn) (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error); + + void (* writev_async) (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + + gboolean (* writev_finish) (GOutputStream *stream, + GAsyncResult *result, + gsize *bytes_written, + GError **error); + /*< private >*/ /* Padding for future expansion */ - void (*_g_reserved1) (void); - void (*_g_reserved2) (void); - void (*_g_reserved3) (void); void (*_g_reserved4) (void); void (*_g_reserved5) (void); void (*_g_reserved6) (void); @@ -147,6 +164,22 @@ gboolean g_output_stream_write_all (GOutputStream *stream, gsize *bytes_written, GCancellable *cancellable, GError **error); + +GLIB_AVAILABLE_IN_2_60 +gboolean g_output_stream_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error); +GLIB_AVAILABLE_IN_2_60 +gboolean g_output_stream_writev_all (GOutputStream *stream, + GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error); + GLIB_AVAILABLE_IN_2_40 gboolean g_output_stream_printf (GOutputStream *stream, gsize *bytes_written, @@ -208,6 +241,35 @@ gboolean g_output_stream_write_all_finish (GOutputStream *stream, gsize *bytes_written, GError **error); +GLIB_AVAILABLE_IN_2_60 +void g_output_stream_writev_async (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GLIB_AVAILABLE_IN_2_60 +gboolean g_output_stream_writev_finish (GOutputStream *stream, + GAsyncResult *result, + gsize *bytes_written, + GError **error); + +GLIB_AVAILABLE_IN_2_60 +void g_output_stream_writev_all_async (GOutputStream *stream, + GOutputVector *vectors, + gsize n_vectors, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GLIB_AVAILABLE_IN_2_60 +gboolean g_output_stream_writev_all_finish (GOutputStream *stream, + GAsyncResult *result, + gsize *bytes_written, + GError **error); + GLIB_AVAILABLE_IN_2_34 void g_output_stream_write_bytes_async (GOutputStream *stream, GBytes *bytes, diff --git a/gio/gpollableoutputstream.c b/gio/gpollableoutputstream.c index 40c649f0d..c17cf9268 100644 --- a/gio/gpollableoutputstream.c +++ b/gio/gpollableoutputstream.c @@ -41,17 +41,23 @@ G_DEFINE_INTERFACE (GPollableOutputStream, g_pollable_output_stream, G_TYPE_OUTPUT_STREAM) -static gboolean g_pollable_output_stream_default_can_poll (GPollableOutputStream *stream); -static gssize g_pollable_output_stream_default_write_nonblocking (GPollableOutputStream *stream, - const void *buffer, - gsize count, - GError **error); +static gboolean g_pollable_output_stream_default_can_poll (GPollableOutputStream *stream); +static gssize g_pollable_output_stream_default_write_nonblocking (GPollableOutputStream *stream, + const void *buffer, + gsize count, + GError **error); +static GPollableReturn g_pollable_output_stream_default_writev_nonblocking (GPollableOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GError **error); static void g_pollable_output_stream_default_init (GPollableOutputStreamInterface *iface) { - iface->can_poll = g_pollable_output_stream_default_can_poll; - iface->write_nonblocking = g_pollable_output_stream_default_write_nonblocking; + iface->can_poll = g_pollable_output_stream_default_can_poll; + iface->write_nonblocking = g_pollable_output_stream_default_write_nonblocking; + iface->writev_nonblocking = g_pollable_output_stream_default_writev_nonblocking; } static gboolean @@ -157,6 +163,67 @@ g_pollable_output_stream_default_write_nonblocking (GPollableOutputStream *stre write_fn (G_OUTPUT_STREAM (stream), buffer, count, NULL, error); } +static GPollableReturn +g_pollable_output_stream_default_writev_nonblocking (GPollableOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GError **error) +{ + gsize _bytes_written = 0; + GPollableOutputStreamInterface *iface = G_POLLABLE_OUTPUT_STREAM_GET_INTERFACE (stream); + gsize i; + GError *err = NULL; + + for (i = 0; i < n_vectors; i++) + { + gssize res; + + /* Would we overflow here? In that case simply return and let the caller + * handle this like a short write */ + if (_bytes_written > G_MAXSIZE - vectors[i].size) + break; + + res = iface->write_nonblocking (stream, vectors[i].buffer, vectors[i].size, &err); + if (res == -1) + { + if (bytes_written) + *bytes_written = _bytes_written; + + /* If something was written already we handle this like a short + * write and assume that the next call would either give the same + * error again or successfully finish writing without errors or data + * loss + */ + if (_bytes_written > 0) + { + g_clear_error (&err); + return G_POLLABLE_RETURN_OK; + } + else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + { + g_clear_error (&err); + return G_POLLABLE_RETURN_WOULD_BLOCK; + } + else + { + g_propagate_error (error, err); + return G_POLLABLE_RETURN_FAILED; + } + } + + _bytes_written += res; + /* if we had a short write break the loop here */ + if (res < vectors[i].size) + break; + } + + if (bytes_written) + *bytes_written = _bytes_written; + + return G_POLLABLE_RETURN_OK; +} + /** * g_pollable_output_stream_write_nonblocking: * @stream: a #GPollableOutputStream @@ -179,7 +246,8 @@ g_pollable_output_stream_default_write_nonblocking (GPollableOutputStream *stre * to having been cancelled. * * Also note that if %G_IO_ERROR_WOULD_BLOCK is returned some underlying - * transports like D/TLS require that you send the same @buffer and @count. + * transports like D/TLS require that you re-send the same @buffer and + * @count in the next write call. * * Virtual: write_nonblocking * Returns: the number of bytes written, or -1 on error (including @@ -221,3 +289,91 @@ g_pollable_output_stream_write_nonblocking (GPollableOutputStream *stream, return res; } + +/** + * g_pollable_output_stream_writev_nonblocking: + * @stream: a #GPollableOutputStream + * @vectors: (array length=n_vectors): the buffer containing the #GOutputVectors to write. + * @n_vectors: the number of vectors to write + * @bytes_written: (out) (optional): location to store the number of bytes that were + * written to the stream + * @cancellable: (nullable): a #GCancellable, or %NULL + * @error: #GError for error reporting, or %NULL to ignore. + * + * Attempts to write the bytes contained in the @n_vectors @vectors to @stream, + * as with g_output_stream_writev(). If @stream is not currently writable, + * this will immediately return %@G_POLLABLE_RETURN_WOULD_BLOCK, and you can + * use g_pollable_output_stream_create_source() to create a #GSource + * that will be triggered when @stream is writable. @error will *not* be + * set in that case. + * + * Note that since this method never blocks, you cannot actually + * use @cancellable to cancel it. However, it will return an error + * if @cancellable has already been cancelled when you call, which + * may happen if you call this method after a source triggers due + * to having been cancelled. + * + * Also note that if %G_POLLABLE_RETURN_WOULD_BLOCK is returned some underlying + * transports like D/TLS require that you re-send the same @vectors and + * @n_vectors in the next write call. + * + * Virtual: writev_nonblocking + * + * Returns: %@G_POLLABLE_RETURN_OK on success, %G_POLLABLE_RETURN_WOULD_BLOCK + * if the stream is not currently writable (and @error is *not* set), or + * %G_POLLABLE_RETURN_FAILED if there was an error in which case @error will + * be set. + * + * Since: 2.60 + */ +GPollableReturn +g_pollable_output_stream_writev_nonblocking (GPollableOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error) +{ + GPollableOutputStreamInterface *iface; + GPollableReturn res; + gsize _bytes_written = 0; + + if (bytes_written) + *bytes_written = 0; + + g_return_val_if_fail (G_IS_POLLABLE_OUTPUT_STREAM (stream), G_POLLABLE_RETURN_FAILED); + g_return_val_if_fail (vectors != NULL || n_vectors == 0, G_POLLABLE_RETURN_FAILED); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_POLLABLE_RETURN_FAILED); + g_return_val_if_fail (error == NULL || *error == NULL, G_POLLABLE_RETURN_FAILED); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return G_POLLABLE_RETURN_FAILED; + + if (n_vectors == 0) + return G_POLLABLE_RETURN_OK; + + iface = G_POLLABLE_OUTPUT_STREAM_GET_INTERFACE (stream); + g_return_val_if_fail (iface->writev_nonblocking != NULL, G_POLLABLE_RETURN_FAILED); + + if (cancellable) + g_cancellable_push_current (cancellable); + + res = iface-> + writev_nonblocking (stream, vectors, n_vectors, &_bytes_written, error); + + if (cancellable) + g_cancellable_pop_current (cancellable); + + if (res == G_POLLABLE_RETURN_FAILED) + g_warn_if_fail (error == NULL || (*error != NULL && !g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))); + else if (res == G_POLLABLE_RETURN_WOULD_BLOCK) + g_warn_if_fail (error == NULL || *error == NULL); + + /* in case of not-OK nothing must've been written */ + g_warn_if_fail (res == G_POLLABLE_RETURN_OK || _bytes_written == 0); + + if (bytes_written) + *bytes_written = _bytes_written; + + return res; +} diff --git a/gio/gpollableoutputstream.h b/gio/gpollableoutputstream.h index bf13584d5..1ef830b57 100644 --- a/gio/gpollableoutputstream.h +++ b/gio/gpollableoutputstream.h @@ -77,6 +77,11 @@ struct _GPollableOutputStreamInterface const void *buffer, gsize count, GError **error); + GPollableReturn (*writev_nonblocking) (GPollableOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GError **error); }; GLIB_AVAILABLE_IN_ALL @@ -98,6 +103,14 @@ gssize g_pollable_output_stream_write_nonblocking (GPollableOutputStream *str GCancellable *cancellable, GError **error); +GLIB_AVAILABLE_IN_2_60 +GPollableReturn g_pollable_output_stream_writev_nonblocking (GPollableOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error); + G_END_DECLS diff --git a/gio/gresource.c b/gio/gresource.c index 2844f4808..e71db43ed 100644 --- a/gio/gresource.c +++ b/gio/gresource.c @@ -285,6 +285,27 @@ enumerate_overlay_dir (const gchar *candidate, return FALSE; } +typedef struct { + gsize size; + guint32 flags; +} InfoData; + +static gboolean +get_overlay_info (const gchar *candidate, + gpointer user_data) +{ + InfoData *info = user_data; + GStatBuf buf; + + if (g_stat (candidate, &buf) < 0) + return FALSE; + + info->size = buf.st_size; + info->flags = G_RESOURCE_FLAGS_NONE; + + return TRUE; +} + static gboolean g_resource_find_overlay (const gchar *path, CheckCandidate check, @@ -1251,6 +1272,17 @@ g_resources_get_info (const gchar *path, gboolean res = FALSE; GList *l; gboolean r_res; + InfoData info; + + if (g_resource_find_overlay (path, get_overlay_info, &info)) + { + if (size) + *size = info.size; + if (flags) + *flags = info.flags; + + return TRUE; + } register_lazy_static_resources (); diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h index 2a76a80bc..9e1d51dba 100644 --- a/gio/gsettingsbackendinternal.h +++ b/gio/gsettingsbackendinternal.h @@ -87,6 +87,8 @@ GType g_null_settings_backend_get_type (void); GType g_memory_settings_backend_get_type (void); +GType g_keyfile_settings_backend_get_type (void); + #ifdef HAVE_COCOA GType g_nextstep_settings_backend_get_type (void); #endif diff --git a/gio/gsocket.c b/gio/gsocket.c index 8ceaa16d3..b1db8e645 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -151,14 +151,14 @@ static gint g_socket_datagram_based_receive_messages (GDatagramBased *self, GInputMessage *messages, guint num_messages, gint flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error); static gint g_socket_datagram_based_send_messages (GDatagramBased *self, GOutputMessage *messages, guint num_messages, gint flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error); static GSource *g_socket_datagram_based_create_source (GDatagramBased *self, @@ -168,7 +168,7 @@ static GIOCondition g_socket_datagram_based_condition_check (GDatagramBased GIOCondition condition); static gboolean g_socket_datagram_based_condition_wait (GDatagramBased *datagram_based, GIOCondition condition, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error); @@ -183,7 +183,7 @@ g_socket_receive_message_with_timeout (GSocket *socket, GSocketControlMessage ***messages, gint *num_messages, gint *flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error); static gint @@ -191,26 +191,15 @@ g_socket_receive_messages_with_timeout (GSocket *socket, GInputMessage *messages, guint num_messages, gint flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error); -static gssize -g_socket_send_message_with_timeout (GSocket *socket, - GSocketAddress *address, - GOutputVector *vectors, - gint num_vectors, - GSocketControlMessage **messages, - gint num_messages, - gint flags, - gint64 timeout, - GCancellable *cancellable, - GError **error); static gint g_socket_send_messages_with_timeout (GSocket *socket, GOutputMessage *messages, guint num_messages, gint flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error); @@ -1157,7 +1146,7 @@ g_socket_datagram_based_receive_messages (GDatagramBased *self, GInputMessage *messages, guint num_messages, gint flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error) { @@ -1165,7 +1154,7 @@ g_socket_datagram_based_receive_messages (GDatagramBased *self, return FALSE; return g_socket_receive_messages_with_timeout (G_SOCKET (self), messages, - num_messages, flags, timeout, + num_messages, flags, timeout_us, cancellable, error); } @@ -1174,7 +1163,7 @@ g_socket_datagram_based_send_messages (GDatagramBased *self, GOutputMessage *messages, guint num_messages, gint flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error) { @@ -1182,7 +1171,7 @@ g_socket_datagram_based_send_messages (GDatagramBased *self, return FALSE; return g_socket_send_messages_with_timeout (G_SOCKET (self), messages, - num_messages, flags, timeout, + num_messages, flags, timeout_us, cancellable, error); } @@ -1210,7 +1199,7 @@ g_socket_datagram_based_condition_check (GDatagramBased *datagram_based, static gboolean g_socket_datagram_based_condition_wait (GDatagramBased *datagram_based, GIOCondition condition, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error) { @@ -1218,7 +1207,7 @@ g_socket_datagram_based_condition_wait (GDatagramBased *datagram_based, return FALSE; return g_socket_condition_timed_wait (G_SOCKET (datagram_based), condition, - timeout, cancellable, error); + timeout_us, cancellable, error); } /** @@ -3017,21 +3006,21 @@ g_socket_get_available_bytes (GSocket *socket) static gboolean block_on_timeout (GSocket *socket, GIOCondition condition, - gint64 timeout, + gint64 timeout_us, gint64 start_time, GCancellable *cancellable, GError **error) { gint64 wait_timeout = -1; - g_return_val_if_fail (timeout != 0, TRUE); + g_return_val_if_fail (timeout_us != 0, TRUE); /* check if we've timed out or how much time to wait at most */ - if (timeout >= 0) + if (timeout_us >= 0) { gint64 elapsed = g_get_monotonic_time () - start_time; - if (elapsed >= timeout) + if (elapsed >= timeout_us) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, @@ -3039,7 +3028,7 @@ block_on_timeout (GSocket *socket, return FALSE; } - wait_timeout = timeout - elapsed; + wait_timeout = timeout_us - elapsed; } return g_socket_condition_timed_wait (socket, condition, wait_timeout, @@ -3050,7 +3039,7 @@ static gssize g_socket_receive_with_timeout (GSocket *socket, guint8 *buffer, gsize size, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error) { @@ -3088,9 +3077,9 @@ g_socket_receive_with_timeout (GSocket *socket, { win32_unset_event_mask (socket, FD_READ); - if (timeout != 0) + if (timeout_us != 0) { - if (!block_on_timeout (socket, G_IO_IN, timeout, start_time, + if (!block_on_timeout (socket, G_IO_IN, timeout_us, start_time, cancellable, error)) return -1; @@ -3249,7 +3238,7 @@ static gssize g_socket_send_with_timeout (GSocket *socket, const guint8 *buffer, gsize size, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error) { @@ -3287,9 +3276,9 @@ g_socket_send_with_timeout (GSocket *socket, { win32_unset_event_mask (socket, FD_WRITE); - if (timeout != 0) + if (timeout_us != 0) { - if (!block_on_timeout (socket, G_IO_OUT, timeout, start_time, + if (!block_on_timeout (socket, G_IO_OUT, timeout_us, start_time, cancellable, error)) return -1; @@ -4126,25 +4115,25 @@ g_socket_condition_wait (GSocket *socket, * g_socket_condition_timed_wait: * @socket: a #GSocket * @condition: a #GIOCondition mask to wait for - * @timeout: the maximum time (in microseconds) to wait, or -1 + * @timeout_us: the maximum time (in microseconds) to wait, or -1 * @cancellable: (nullable): a #GCancellable, or %NULL * @error: a #GError pointer, or %NULL * - * Waits for up to @timeout microseconds for @condition to become true + * Waits for up to @timeout_us microseconds for @condition to become true * on @socket. If the condition is met, %TRUE is returned. * * If @cancellable is cancelled before the condition is met, or if - * @timeout (or the socket's #GSocket:timeout) is reached before the + * @timeout_us (or the socket's #GSocket:timeout) is reached before the * condition is met, then %FALSE is returned and @error, if non-%NULL, * is set to the appropriate value (%G_IO_ERROR_CANCELLED or * %G_IO_ERROR_TIMED_OUT). * * If you don't want a timeout, use g_socket_condition_wait(). - * (Alternatively, you can pass -1 for @timeout.) + * (Alternatively, you can pass -1 for @timeout_us.) * - * Note that although @timeout is in microseconds for consistency with + * Note that although @timeout_us is in microseconds for consistency with * other GLib APIs, this function actually only has millisecond - * resolution, and the behavior is undefined if @timeout is not an + * resolution, and the behavior is undefined if @timeout_us is not an * exact number of milliseconds. * * Returns: %TRUE if the condition was met, %FALSE otherwise @@ -4154,11 +4143,12 @@ g_socket_condition_wait (GSocket *socket, gboolean g_socket_condition_timed_wait (GSocket *socket, GIOCondition condition, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error) { gint64 start_time; + gint64 timeout_ms; g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); @@ -4169,10 +4159,12 @@ g_socket_condition_timed_wait (GSocket *socket, return FALSE; if (socket->priv->timeout && - (timeout < 0 || socket->priv->timeout < timeout / G_USEC_PER_SEC)) - timeout = (gint64) socket->priv->timeout * 1000; - else if (timeout != -1) - timeout = timeout / 1000; + (timeout_us < 0 || socket->priv->timeout < timeout_us / G_USEC_PER_SEC)) + timeout_ms = (gint64) socket->priv->timeout * 1000; + else if (timeout_us != -1) + timeout_ms = timeout_us / 1000; + else + timeout_ms = -1; start_time = g_get_monotonic_time (); @@ -4195,8 +4187,8 @@ g_socket_condition_timed_wait (GSocket *socket, if (g_cancellable_make_pollfd (cancellable, &cancel_fd)) events[num_events++] = (WSAEVENT)cancel_fd.fd; - if (timeout == -1) - timeout = WSA_INFINITE; + if (timeout_ms == -1) + timeout_ms = WSA_INFINITE; g_mutex_lock (&socket->priv->win32_source_lock); current_condition = update_condition_unlocked (socket); @@ -4208,7 +4200,7 @@ g_socket_condition_timed_wait (GSocket *socket, socket->priv->waiting_result = 0; g_mutex_unlock (&socket->priv->win32_source_lock); - res = WSAWaitForMultipleEvents (num_events, events, FALSE, timeout, FALSE); + res = WSAWaitForMultipleEvents (num_events, events, FALSE, timeout_ms, FALSE); g_mutex_lock (&socket->priv->win32_source_lock); socket->priv->waiting = FALSE; @@ -4217,9 +4209,9 @@ g_socket_condition_timed_wait (GSocket *socket, } else { - if (timeout != WSA_INFINITE) + if (timeout_ms != WSA_INFINITE) { - if (!g_cond_wait_until (&socket->priv->win32_source_cond, &socket->priv->win32_source_lock, timeout)) + if (!g_cond_wait_until (&socket->priv->win32_source_cond, &socket->priv->win32_source_lock, timeout_ms)) { res = WSA_WAIT_TIMEOUT; break; @@ -4258,11 +4250,11 @@ g_socket_condition_timed_wait (GSocket *socket, current_condition = update_condition_unlocked (socket); - if (timeout != WSA_INFINITE) + if (timeout_ms != WSA_INFINITE) { - timeout -= (g_get_monotonic_time () - start_time) * 1000; - if (timeout < 0) - timeout = 0; + timeout_ms -= (g_get_monotonic_time () - start_time) * 1000; + if (timeout_ms < 0) + timeout_ms = 0; } } g_mutex_unlock (&socket->priv->win32_source_lock); @@ -4288,16 +4280,16 @@ g_socket_condition_timed_wait (GSocket *socket, while (TRUE) { int errsv; - result = g_poll (poll_fd, num, timeout); + result = g_poll (poll_fd, num, timeout_ms); errsv = errno; if (result != -1 || errsv != EINTR) break; - if (timeout != -1) + if (timeout_ms != -1) { - timeout -= (g_get_monotonic_time () - start_time) / 1000; - if (timeout < 0) - timeout = 0; + timeout_ms -= (g_get_monotonic_time () - start_time) / 1000; + if (timeout_ms < 0) + timeout_ms = 0; } } @@ -4548,7 +4540,7 @@ input_message_from_msghdr (const struct msghdr *msg, * @messages: (array length=num_messages) (nullable): a pointer to an * array of #GSocketControlMessages, or %NULL. * @num_messages: number of elements in @messages, or -1. - * @flags: an int containing #GSocketMsgFlags flags + * @flags: (type GSocketMsgFlags): an int containing #GSocketMsgFlags flags * @cancellable: (nullable): a %GCancellable or %NULL * @error: #GError for error reporting, or %NULL to ignore. * @@ -4606,22 +4598,61 @@ g_socket_send_message (GSocket *socket, GCancellable *cancellable, GError **error) { - return g_socket_send_message_with_timeout (socket, address, - vectors, num_vectors, - messages, num_messages, flags, - socket->priv->blocking ? -1 : 0, - cancellable, error); + GPollableReturn res; + gsize bytes_written = 0; + + res = g_socket_send_message_with_timeout (socket, address, + vectors, num_vectors, + messages, num_messages, flags, + socket->priv->blocking ? -1 : 0, + &bytes_written, + cancellable, error); + + if (res == G_POLLABLE_RETURN_WOULD_BLOCK) + socket_set_error_lazy (error, EWOULDBLOCK, _("Error sending message: %s")); + + return res == G_POLLABLE_RETURN_OK ? bytes_written : -1; } -static gssize +/** + * g_socket_send_message_with_timeout: + * @socket: a #GSocket + * @address: (nullable): a #GSocketAddress, or %NULL + * @vectors: (array length=num_vectors): an array of #GOutputVector structs + * @num_vectors: the number of elements in @vectors, or -1 + * @messages: (array length=num_messages) (nullable): a pointer to an + * array of #GSocketControlMessages, or %NULL. + * @num_messages: number of elements in @messages, or -1. + * @flags: (type GSocketMsgFlags): an int containing #GSocketMsgFlags flags + * @timeout_us: the maximum time (in microseconds) to wait, or -1 + * @bytes_written: (out) (optional): location to store the number of bytes that were written to the socket + * @cancellable: (nullable): a %GCancellable or %NULL + * @error: #GError for error reporting, or %NULL to ignore. + * + * This behaves exactly the same as g_socket_send_message(), except that + * the choice of timeout behavior is determined by the @timeout_us argument + * rather than by @socket's properties. + * + * On error %G_POLLABLE_RETURN_FAILED is returned and @error is set accordingly, or + * if the socket is currently not writable %G_POLLABLE_RETURN_WOULD_BLOCK is + * returned. @bytes_written will contain 0 in both cases. + * + * Returns: %G_POLLABLE_RETURN_OK if all data was successfully written, + * %G_POLLABLE_RETURN_WOULD_BLOCK if the socket is currently not writable, or + * %G_POLLABLE_RETURN_FAILED if an error happened and @error is set. + * + * Since: 2.60 + */ +GPollableReturn g_socket_send_message_with_timeout (GSocket *socket, GSocketAddress *address, - GOutputVector *vectors, + const GOutputVector *vectors, gint num_vectors, GSocketControlMessage **messages, gint num_messages, gint flags, - gint64 timeout, + gint64 timeout_us, + gsize *bytes_written, GCancellable *cancellable, GError **error) { @@ -4629,23 +4660,26 @@ g_socket_send_message_with_timeout (GSocket *socket, char zero; gint64 start_time; - g_return_val_if_fail (G_IS_SOCKET (socket), -1); - g_return_val_if_fail (address == NULL || G_IS_SOCKET_ADDRESS (address), -1); - g_return_val_if_fail (num_vectors == 0 || vectors != NULL, -1); - g_return_val_if_fail (num_messages == 0 || messages != NULL, -1); - g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), -1); - g_return_val_if_fail (error == NULL || *error == NULL, -1); + if (bytes_written) + *bytes_written = 0; + + g_return_val_if_fail (G_IS_SOCKET (socket), G_POLLABLE_RETURN_FAILED); + g_return_val_if_fail (address == NULL || G_IS_SOCKET_ADDRESS (address), G_POLLABLE_RETURN_FAILED); + g_return_val_if_fail (num_vectors == 0 || vectors != NULL, G_POLLABLE_RETURN_FAILED); + g_return_val_if_fail (num_messages == 0 || messages != NULL, G_POLLABLE_RETURN_FAILED); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_POLLABLE_RETURN_FAILED); + g_return_val_if_fail (error == NULL || *error == NULL, G_POLLABLE_RETURN_FAILED); start_time = g_get_monotonic_time (); if (!check_socket (socket, error)) - return -1; + return G_POLLABLE_RETURN_FAILED; if (!check_timeout (socket, error)) - return -1; + return G_POLLABLE_RETURN_FAILED; if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return -1; + return G_POLLABLE_RETURN_FAILED; if (num_vectors == -1) { @@ -4681,7 +4715,7 @@ g_socket_send_message_with_timeout (GSocket *socket, GError *child_error = NULL; output_message.address = address; - output_message.vectors = vectors; + output_message.vectors = (GOutputVector *) vectors; output_message.num_vectors = num_vectors; output_message.bytes_sent = 0; output_message.control_messages = messages; @@ -4692,7 +4726,7 @@ g_socket_send_message_with_timeout (GSocket *socket, if (child_error != NULL) { g_propagate_error (error, child_error); - return -1; + return G_POLLABLE_RETURN_FAILED; } while (1) @@ -4705,24 +4739,30 @@ g_socket_send_message_with_timeout (GSocket *socket, if (errsv == EINTR) continue; - if (timeout != 0 && - (errsv == EWOULDBLOCK || - errsv == EAGAIN)) + if (errsv == EWOULDBLOCK || errsv == EAGAIN) { - if (!block_on_timeout (socket, G_IO_OUT, timeout, start_time, - cancellable, error)) - return -1; + if (timeout_us != 0) + { + if (!block_on_timeout (socket, G_IO_OUT, timeout_us, start_time, + cancellable, error)) + return G_POLLABLE_RETURN_FAILED; - continue; + continue; + } + + return G_POLLABLE_RETURN_WOULD_BLOCK; } - socket_set_error_lazy (error, errsv, _("Error sending message: %s")); - return -1; + socket_set_error_lazy (error, errsv, _("Error sending message: %s")); + return G_POLLABLE_RETURN_FAILED; } break; } - return result; + if (bytes_written) + *bytes_written = result; + + return G_POLLABLE_RETURN_OK; } #else { @@ -4741,7 +4781,7 @@ g_socket_send_message_with_timeout (GSocket *socket, { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("GSocketControlMessage not supported on Windows")); - return -1; + return G_POLLABLE_RETURN_FAILED; } /* iov */ @@ -4758,7 +4798,7 @@ g_socket_send_message_with_timeout (GSocket *socket, { addrlen = g_socket_address_get_native_size (address); if (!g_socket_address_to_native (address, &addr, sizeof addr, error)) - return -1; + return G_POLLABLE_RETURN_FAILED; } while (1) @@ -4786,23 +4826,27 @@ g_socket_send_message_with_timeout (GSocket *socket, { win32_unset_event_mask (socket, FD_WRITE); - if (timeout != 0) + if (timeout_us != 0) { - if (!block_on_timeout (socket, G_IO_OUT, timeout, + if (!block_on_timeout (socket, G_IO_OUT, timeout_us, start_time, cancellable, error)) - return -1; + return G_POLLABLE_RETURN_FAILED; continue; } + + return G_POLLABLE_RETURN_WOULD_BLOCK; } socket_set_error_lazy (error, errsv, _("Error sending message: %s")); - return -1; + return G_POLLABLE_RETURN_FAILED; } break; } - return bytes_sent; + if (bytes_written) + *bytes_written = bytes_sent; + return G_POLLABLE_RETURN_OK; } #endif } @@ -4812,7 +4856,7 @@ g_socket_send_message_with_timeout (GSocket *socket, * @socket: a #GSocket * @messages: (array length=num_messages): an array of #GOutputMessage structs * @num_messages: the number of elements in @messages - * @flags: an int containing #GSocketMsgFlags flags + * @flags: (type GSocketMsgFlags): an int containing #GSocketMsgFlags flags * @cancellable: (nullable): a %GCancellable or %NULL * @error: #GError for error reporting, or %NULL to ignore. * @@ -4877,7 +4921,7 @@ g_socket_send_messages_with_timeout (GSocket *socket, GOutputMessage *messages, guint num_messages, gint flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error) { @@ -4951,11 +4995,11 @@ g_socket_send_messages_with_timeout (GSocket *socket, if (errsv == EINTR) continue; - if (timeout != 0 && + if (timeout_us != 0 && (errsv == EWOULDBLOCK || errsv == EAGAIN)) { - if (!block_on_timeout (socket, G_IO_OUT, timeout, start_time, + if (!block_on_timeout (socket, G_IO_OUT, timeout_us, start_time, cancellable, error)) { if (num_sent > 0) @@ -4993,26 +5037,34 @@ g_socket_send_messages_with_timeout (GSocket *socket, gint i; gint64 wait_timeout; - wait_timeout = timeout; + wait_timeout = timeout_us; for (i = 0; i < num_messages; ++i) { GOutputMessage *msg = &messages[i]; GError *msg_error = NULL; + GPollableReturn pollable_result; + gsize bytes_written = 0; - result = g_socket_send_message_with_timeout (socket, msg->address, - msg->vectors, - msg->num_vectors, - msg->control_messages, - msg->num_control_messages, - flags, wait_timeout, - cancellable, &msg_error); + pollable_result = g_socket_send_message_with_timeout (socket, msg->address, + msg->vectors, + msg->num_vectors, + msg->control_messages, + msg->num_control_messages, + flags, wait_timeout, + &bytes_written, + cancellable, &msg_error); + + if (pollable_result == G_POLLABLE_RETURN_WOULD_BLOCK) + socket_set_error_lazy (&msg_error, EWOULDBLOCK, _("Error sending message: %s")); + + result = pollable_result == G_POLLABLE_RETURN_OK ? bytes_written : -1; /* check if we've timed out or how much time to wait at most */ - if (timeout > 0) + if (timeout_us > 0) { gint64 elapsed = g_get_monotonic_time () - start_time; - wait_timeout = MAX (timeout - elapsed, 1); + wait_timeout = MAX (timeout_us - elapsed, 1); } if (result < 0) @@ -5101,7 +5153,7 @@ g_socket_receive_message_with_timeout (GSocket *socket, GSocketControlMessage ***messages, gint *num_messages, gint *flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error) { @@ -5182,11 +5234,11 @@ g_socket_receive_message_with_timeout (GSocket *socket, if (errsv == EINTR) continue; - if (timeout != 0 && + if (timeout_us != 0 && (errsv == EWOULDBLOCK || errsv == EAGAIN)) { - if (!block_on_timeout (socket, G_IO_IN, timeout, start_time, + if (!block_on_timeout (socket, G_IO_IN, timeout_us, start_time, cancellable, error)) return -1; @@ -5256,9 +5308,9 @@ g_socket_receive_message_with_timeout (GSocket *socket, { win32_unset_event_mask (socket, FD_READ); - if (timeout != 0) + if (timeout_us != 0) { - if (!block_on_timeout (socket, G_IO_IN, timeout, + if (!block_on_timeout (socket, G_IO_IN, timeout_us, start_time, cancellable, error)) return -1; @@ -5298,7 +5350,7 @@ g_socket_receive_message_with_timeout (GSocket *socket, * @socket: a #GSocket * @messages: (array length=num_messages): an array of #GInputMessage structs * @num_messages: the number of elements in @messages - * @flags: an int containing #GSocketMsgFlags flags for the overall operation + * @flags: (type GSocketMsgFlags): an int containing #GSocketMsgFlags flags for the overall operation * @cancellable: (nullable): a %GCancellable or %NULL * @error: #GError for error reporting, or %NULL to ignore * @@ -5382,7 +5434,7 @@ g_socket_receive_messages_with_timeout (GSocket *socket, GInputMessage *messages, guint num_messages, gint flags, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error) { @@ -5469,11 +5521,11 @@ g_socket_receive_messages_with_timeout (GSocket *socket, if (errsv == EINTR) continue; - if (timeout != 0 && + if (timeout_us != 0 && (errsv == EWOULDBLOCK || errsv == EAGAIN)) { - if (!block_on_timeout (socket, G_IO_IN, timeout, start_time, + if (!block_on_timeout (socket, G_IO_IN, timeout_us, start_time, cancellable, error)) { if (num_received > 0) @@ -5519,7 +5571,7 @@ g_socket_receive_messages_with_timeout (GSocket *socket, guint i; gint64 wait_timeout; - wait_timeout = timeout; + wait_timeout = timeout_us; for (i = 0; i < num_messages; i++) { @@ -5541,10 +5593,10 @@ g_socket_receive_messages_with_timeout (GSocket *socket, &msg_error); /* check if we've timed out or how much time to wait at most */ - if (timeout > 0) + if (timeout_us > 0) { gint64 elapsed = g_get_monotonic_time () - start_time; - wait_timeout = MAX (timeout - elapsed, 1); + wait_timeout = MAX (timeout_us - elapsed, 1); } if (len >= 0) @@ -5584,7 +5636,7 @@ g_socket_receive_messages_with_timeout (GSocket *socket, * which may be filled with an array of #GSocketControlMessages, or %NULL * @num_messages: (out): a pointer which will be filled with the number of * elements in @messages, or %NULL - * @flags: (inout): a pointer to an int containing #GSocketMsgFlags flags + * @flags: (type GSocketMsgFlags): (inout): a pointer to an int containing #GSocketMsgFlags flags * @cancellable: a %GCancellable or %NULL * @error: a #GError pointer, or %NULL * diff --git a/gio/gsocket.h b/gio/gsocket.h index a65cbc22f..97411355d 100644 --- a/gio/gsocket.h +++ b/gio/gsocket.h @@ -192,7 +192,7 @@ gboolean g_socket_condition_wait (GSocket GLIB_AVAILABLE_IN_2_32 gboolean g_socket_condition_timed_wait (GSocket *socket, GIOCondition condition, - gint64 timeout, + gint64 timeout_us, GCancellable *cancellable, GError **error); GLIB_AVAILABLE_IN_ALL @@ -298,7 +298,18 @@ gssize g_socket_send_with_blocking (GSocket gboolean blocking, GCancellable *cancellable, GError **error); - +GLIB_AVAILABLE_IN_2_60 +GPollableReturn g_socket_send_message_with_timeout (GSocket *socket, + GSocketAddress *address, + const GOutputVector *vectors, + gint num_vectors, + GSocketControlMessage **messages, + gint num_messages, + gint flags, + gint64 timeout_us, + gsize *bytes_written, + GCancellable *cancellable, + GError **error); GLIB_AVAILABLE_IN_2_36 gboolean g_socket_get_option (GSocket *socket, gint level, diff --git a/gio/gsocketoutputstream.c b/gio/gsocketoutputstream.c index de1f4226b..fcca56efc 100644 --- a/gio/gsocketoutputstream.c +++ b/gio/gsocketoutputstream.c @@ -125,14 +125,43 @@ g_socket_output_stream_write (GOutputStream *stream, GCancellable *cancellable, GError **error) { - GSocketOutputStream *onput_stream = G_SOCKET_OUTPUT_STREAM (stream); + GSocketOutputStream *output_stream = G_SOCKET_OUTPUT_STREAM (stream); - return g_socket_send_with_blocking (onput_stream->priv->socket, + return g_socket_send_with_blocking (output_stream->priv->socket, buffer, count, TRUE, cancellable, error); } static gboolean +g_socket_output_stream_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error) +{ + GSocketOutputStream *output_stream = G_SOCKET_OUTPUT_STREAM (stream); + GPollableReturn res; + + /* Clamp the number of vectors if more given than we can write in one go. + * The caller has to handle short writes anyway. + */ + if (n_vectors > G_MAXINT) + n_vectors = G_MAXINT; + + res = g_socket_send_message_with_timeout (output_stream->priv->socket, NULL, + vectors, n_vectors, + NULL, 0, G_SOCKET_MSG_NONE, + -1, bytes_written, + cancellable, error); + + /* we have a non-zero timeout so this can't happen */ + g_assert (res != G_POLLABLE_RETURN_WOULD_BLOCK); + + return res == G_POLLABLE_RETURN_OK; +} + +static gboolean g_socket_output_stream_pollable_is_writable (GPollableOutputStream *pollable) { GSocketOutputStream *output_stream = G_SOCKET_OUTPUT_STREAM (pollable); @@ -153,6 +182,27 @@ g_socket_output_stream_pollable_write_nonblocking (GPollableOutputStream *polla NULL, error); } +static GPollableReturn +g_socket_output_stream_pollable_writev_nonblocking (GPollableOutputStream *pollable, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GError **error) +{ + GSocketOutputStream *output_stream = G_SOCKET_OUTPUT_STREAM (pollable); + + /* Clamp the number of vectors if more given than we can write in one go. + * The caller has to handle short writes anyway. + */ + if (n_vectors > G_MAXINT) + n_vectors = G_MAXINT; + + return g_socket_send_message_with_timeout (output_stream->priv->socket, + NULL, vectors, n_vectors, + NULL, 0, G_SOCKET_MSG_NONE, 0, + bytes_written, NULL, error); +} + static GSource * g_socket_output_stream_pollable_create_source (GPollableOutputStream *pollable, GCancellable *cancellable) @@ -191,6 +241,7 @@ g_socket_output_stream_class_init (GSocketOutputStreamClass *klass) gobject_class->set_property = g_socket_output_stream_set_property; goutputstream_class->write_fn = g_socket_output_stream_write; + goutputstream_class->writev_fn = g_socket_output_stream_writev; g_object_class_install_property (gobject_class, PROP_SOCKET, g_param_spec_object ("socket", @@ -214,6 +265,7 @@ g_socket_output_stream_pollable_iface_init (GPollableOutputStreamInterface *ifac iface->is_writable = g_socket_output_stream_pollable_is_writable; iface->create_source = g_socket_output_stream_pollable_create_source; iface->write_nonblocking = g_socket_output_stream_pollable_write_nonblocking; + iface->writev_nonblocking = g_socket_output_stream_pollable_writev_nonblocking; } static void diff --git a/gio/gtask.c b/gio/gtask.c index a2f316d2e..aa98f752c 100644 --- a/gio/gtask.c +++ b/gio/gtask.c @@ -1132,7 +1132,8 @@ g_task_get_check_cancellable (GTask *task) { g_return_val_if_fail (G_IS_TASK (task), FALSE); - return task->check_cancellable; + /* Convert from a bit field to a boolean. */ + return task->check_cancellable ? TRUE : FALSE; } /** @@ -1149,7 +1150,8 @@ g_task_get_return_on_cancel (GTask *task) { g_return_val_if_fail (G_IS_TASK (task), FALSE); - return task->return_on_cancel; + /* Convert from a bit field to a boolean. */ + return task->return_on_cancel ? TRUE : FALSE; } /** @@ -1952,7 +1954,8 @@ g_task_get_completed (GTask *task) { g_return_val_if_fail (G_IS_TASK (task), FALSE); - return task->completed; + /* Convert from a bit field to a boolean. */ + return task->completed ? TRUE : FALSE; } /** @@ -2055,7 +2058,7 @@ g_task_get_property (GObject *object, switch ((GTaskProperty) prop_id) { case PROP_COMPLETED: - g_value_set_boolean (value, task->completed); + g_value_set_boolean (value, g_task_get_completed (task)); break; } } diff --git a/gio/gtlsconnection.c b/gio/gtlsconnection.c index a1e98c0c9..02a3098c1 100644 --- a/gio/gtlsconnection.c +++ b/gio/gtlsconnection.c @@ -323,8 +323,8 @@ g_tls_connection_class_init (GTlsConnectionClass *klass) * let the user decide whether or not to accept the certificate, you * would have to return %FALSE from the signal handler on the first * attempt, and then after the connection attempt returns a - * %G_TLS_ERROR_HANDSHAKE, you can interact with the user, and if - * the user decides to accept the certificate, remember that fact, + * %G_TLS_ERROR_BAD_CERTIFICATE, you can interact with the user, and + * if the user decides to accept the certificate, remember that fact, * create a new connection, and return %TRUE from the signal handler * the next time. * diff --git a/gio/gunixoutputstream.c b/gio/gunixoutputstream.c index 3cf96cf19..5536e00b4 100644 --- a/gio/gunixoutputstream.c +++ b/gio/gunixoutputstream.c @@ -26,6 +26,7 @@ #include <errno.h> #include <stdio.h> #include <fcntl.h> +#include <sys/uio.h> #include <glib.h> #include <glib/gstdio.h> @@ -91,6 +92,12 @@ static gssize g_unix_output_stream_write (GOutputStream *stream, gsize count, GCancellable *cancellable, GError **error); +static gboolean g_unix_output_stream_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error); static gboolean g_unix_output_stream_close (GOutputStream *stream, GCancellable *cancellable, GError **error); @@ -107,6 +114,11 @@ static gboolean g_unix_output_stream_pollable_can_poll (GPollableOutputStre static gboolean g_unix_output_stream_pollable_is_writable (GPollableOutputStream *stream); static GSource *g_unix_output_stream_pollable_create_source (GPollableOutputStream *stream, GCancellable *cancellable); +static GPollableReturn g_unix_output_stream_pollable_writev_nonblocking (GPollableOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GError **error); static void g_unix_output_stream_class_init (GUnixOutputStreamClass *klass) @@ -118,6 +130,7 @@ g_unix_output_stream_class_init (GUnixOutputStreamClass *klass) gobject_class->set_property = g_unix_output_stream_set_property; stream_class->write_fn = g_unix_output_stream_write; + stream_class->writev_fn = g_unix_output_stream_writev; stream_class->close_fn = g_unix_output_stream_close; stream_class->close_async = g_unix_output_stream_close_async; stream_class->close_finish = g_unix_output_stream_close_finish; @@ -159,6 +172,7 @@ g_unix_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface) iface->can_poll = g_unix_output_stream_pollable_can_poll; iface->is_writable = g_unix_output_stream_pollable_is_writable; iface->create_source = g_unix_output_stream_pollable_create_source; + iface->writev_nonblocking = g_unix_output_stream_pollable_writev_nonblocking; } static void @@ -325,19 +339,18 @@ g_unix_output_stream_write (GOutputStream *stream, GUnixOutputStream *unix_stream; gssize res = -1; GPollFD poll_fds[2]; - int nfds; + int nfds = 0; int poll_ret; unix_stream = G_UNIX_OUTPUT_STREAM (stream); poll_fds[0].fd = unix_stream->priv->fd; poll_fds[0].events = G_IO_OUT; + nfds++; if (unix_stream->priv->is_pipe_or_socket && g_cancellable_make_pollfd (cancellable, &poll_fds[1])) - nfds = 2; - else - nfds = 1; + nfds++; while (1) { @@ -387,6 +400,116 @@ g_unix_output_stream_write (GOutputStream *stream, return res; } +/* Macro to check if struct iovec and GOutputVector have the same ABI */ +#define G_OUTPUT_VECTOR_IS_IOVEC (sizeof (struct iovec) == sizeof (GOutputVector) && \ + sizeof ((struct iovec *) 0)->iov_base == sizeof ((GOutputVector *) 0)->buffer && \ + G_STRUCT_OFFSET (struct iovec, iov_base) == G_STRUCT_OFFSET (GOutputVector, buffer) && \ + sizeof ((struct iovec *) 0)->iov_len == sizeof((GOutputVector *) 0)->size && \ + G_STRUCT_OFFSET (struct iovec, iov_len) == G_STRUCT_OFFSET (GOutputVector, size)) + +static gboolean +g_unix_output_stream_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error) +{ + GUnixOutputStream *unix_stream; + gssize res = -1; + GPollFD poll_fds[2]; + int nfds = 0; + int poll_ret; + struct iovec *iov; + + if (bytes_written) + *bytes_written = 0; + + /* Clamp to G_MAXINT as writev() takes an integer for the number of vectors. + * We handle this like a short write in this case + */ + if (n_vectors > G_MAXINT) + n_vectors = G_MAXINT; + + unix_stream = G_UNIX_OUTPUT_STREAM (stream); + + if (G_OUTPUT_VECTOR_IS_IOVEC) + { + /* ABI is compatible */ + iov = (struct iovec *) vectors; + } + else + { + gsize i; + + /* ABI is incompatible */ + iov = g_newa (struct iovec, n_vectors); + for (i = 0; i < n_vectors; i++) + { + iov[i].iov_base = (void *)vectors[i].buffer; + iov[i].iov_len = vectors[i].size; + } + } + + poll_fds[0].fd = unix_stream->priv->fd; + poll_fds[0].events = G_IO_OUT; + nfds++; + + if (unix_stream->priv->is_pipe_or_socket && + g_cancellable_make_pollfd (cancellable, &poll_fds[1])) + nfds++; + + while (1) + { + int errsv; + + poll_fds[0].revents = poll_fds[1].revents = 0; + do + { + poll_ret = g_poll (poll_fds, nfds, -1); + errsv = errno; + } + while (poll_ret == -1 && errsv == EINTR); + + if (poll_ret == -1) + { + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error writing to file descriptor: %s"), + g_strerror (errsv)); + break; + } + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + break; + + if (!poll_fds[0].revents) + continue; + + res = writev (unix_stream->priv->fd, iov, n_vectors); + errsv = errno; + if (res == -1) + { + if (errsv == EINTR || errsv == EAGAIN) + continue; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error writing to file descriptor: %s"), + g_strerror (errsv)); + } + + if (bytes_written) + *bytes_written = res; + + break; + } + + if (nfds == 2) + g_cancellable_release_fd (cancellable); + return res != -1; +} + static gboolean g_unix_output_stream_close (GOutputStream *stream, GCancellable *cancellable, @@ -494,3 +617,70 @@ g_unix_output_stream_pollable_create_source (GPollableOutputStream *stream, return pollable_source; } + +static GPollableReturn +g_unix_output_stream_pollable_writev_nonblocking (GPollableOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GError **error) +{ + GUnixOutputStream *unix_stream = G_UNIX_OUTPUT_STREAM (stream); + struct iovec *iov; + gssize res = -1; + + if (!g_pollable_output_stream_is_writable (stream)) + { + *bytes_written = 0; + return G_POLLABLE_RETURN_WOULD_BLOCK; + } + + /* Clamp to G_MAXINT as writev() takes an integer for the number of vectors. + * We handle this like a short write in this case + */ + if (n_vectors > G_MAXINT) + n_vectors = G_MAXINT; + + if (G_OUTPUT_VECTOR_IS_IOVEC) + { + /* ABI is compatible */ + iov = (struct iovec *) vectors; + } + else + { + gsize i; + + /* ABI is incompatible */ + iov = g_newa (struct iovec, n_vectors); + for (i = 0; i < n_vectors; i++) + { + iov[i].iov_base = (void *)vectors[i].buffer; + iov[i].iov_len = vectors[i].size; + } + } + + while (1) + { + int errsv; + + res = writev (unix_stream->priv->fd, iov, n_vectors); + errsv = errno; + if (res == -1) + { + if (errsv == EINTR) + continue; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error writing to file descriptor: %s"), + g_strerror (errsv)); + } + + if (bytes_written) + *bytes_written = res; + + break; + } + + return res != -1 ? G_POLLABLE_RETURN_OK : G_POLLABLE_RETURN_FAILED; +} diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c index 499bbb351..9f335b370 100644 --- a/gio/gwin32appinfo.c +++ b/gio/gwin32appinfo.c @@ -1426,7 +1426,7 @@ collect_capable_apps_from_clients (GPtrArray *capable_apps, GWin32RegistrySubkeyIter subkey_iter; GWin32RegistryKey *system_client_type; GWin32RegistryValueType default_type; - gunichar2 *default_value; + gunichar2 *default_value = NULL; gunichar2 *client_name; gsize client_name_len; diff --git a/gio/inotify/Makefile.am b/gio/inotify/Makefile.am deleted file mode 100644 index 6dd9136b4..000000000 --- a/gio/inotify/Makefile.am +++ /dev/null @@ -1,32 +0,0 @@ -include $(top_srcdir)/glib.mk - -noinst_LTLIBRARIES += libinotify.la - -libinotify_la_SOURCES = \ - inotify-kernel.c \ - inotify-sub.c \ - inotify-path.c \ - inotify-missing.c \ - inotify-helper.c \ - inotify-kernel.h \ - inotify-missing.h \ - inotify-path.h \ - inotify-sub.h \ - inotify-helper.h \ - ginotifyfilemonitor.c \ - ginotifyfilemonitor.h \ - $(NULL) - -libinotify_la_CFLAGS = \ - $(GLIB_HIDDEN_VISIBILITY_CFLAGS) \ - -DG_LOG_DOMAIN=\"GLib-GIO\" \ - $(gio_INCLUDES) \ - $(GLIB_DEBUG_FLAGS) \ - -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \ - -DGIO_COMPILATION \ - -DG_DISABLE_DEPRECATED - -libinotify_la_LIBADD = \ - $(top_builddir)/glib/libglib-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(NULL)
\ No newline at end of file diff --git a/gio/kqueue/Makefile.am b/gio/kqueue/Makefile.am deleted file mode 100644 index 24e9724e5..000000000 --- a/gio/kqueue/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -include $(top_srcdir)/glib.mk - -noinst_LTLIBRARIES += libkqueue.la - -libkqueue_la_SOURCES = \ - gkqueuefilemonitor.c \ - kqueue-helper.c \ - kqueue-helper.h \ - kqueue-missing.c \ - dep-list.c \ - dep-list.h \ - $(NULL) - -libkqueue_la_CFLAGS = \ - $(GLIB_HIDDEN_VISIBILITY_CFLAGS) \ - -DG_LOG_DOMAIN=\"GLib-GIO\" \ - $(gio_INCLUDES) \ - $(GLIB_DEBUG_FLAGS) \ - -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \ - -DGIO_COMPILATION \ - -DG_DISABLE_DEPRECATED diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am deleted file mode 100644 index e8baa0101..000000000 --- a/gio/tests/Makefile.am +++ /dev/null @@ -1,709 +0,0 @@ -include $(top_srcdir)/glib-tap.mk - -dist_uninstalled_test_data = -test_ltlibraries = - -SUBDIRS = gdbus-object-manager-example services modules - -LDADD = \ - $(top_builddir)/gio/libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(NULL) - -AM_CPPFLAGS = $(gio_INCLUDES) $(GLIB_DEBUG_FLAGS) -I$(top_builddir)/gio -I$(top_srcdir)/gio -DEFS = -DG_LOG_DOMAIN=\"GLib-GIO\" -DTEST_SERVICES=\""$(abs_top_builddir)/gio/tests/services"\" -AM_CFLAGS = $(GLIB_WARN_CFLAGS) -AM_TESTS_ENVIRONMENT += \ - GIO_MODULE_DIR= \ - GIO_LAUNCH_DESKTOP="$(top_builddir)/gio/gio-launch-desktop" - -# ----------------------------------------------------------------------------- -# Test programs buildable on all platforms - -test_programs = \ - appmonitor \ - async-close-output-stream \ - async-splice-output-stream \ - buffered-input-stream \ - buffered-output-stream \ - cancellable \ - contexts \ - contenttype \ - converter-stream \ - credentials \ - data-input-stream \ - data-output-stream \ - fileattributematcher \ - filter-streams \ - giomodule \ - gsubprocess \ - g-file \ - g-file-info \ - g-icon \ - gdbus-addresses \ - gdbus-message \ - inet-address \ - io-stream \ - memory-input-stream \ - memory-output-stream \ - monitor \ - mount-operation \ - network-monitor \ - network-monitor-race \ - permission \ - pollable \ - proxy-test \ - readwrite \ - simple-async-result \ - simple-proxy \ - sleepy-stream \ - socket \ - socket-listener \ - socket-service \ - srvtarget \ - task \ - tls-database \ - tls-interaction \ - vfs \ - volumemonitor \ - glistmodel \ - testfilemonitor \ - $(NULL) - -uninstalled_test_programs = \ - $(NULL) - -dist_test_data = \ - contexts.c \ - g-icon.c \ - $(NULL) - -test_data = \ - $(NULL) - -uninstalled_test_extra_programs = \ - gio-du \ - echo-server \ - filter-cat \ - gapplication-example-actions \ - gapplication-example-cmdline \ - gapplication-example-cmdline2 \ - gapplication-example-cmdline3 \ - gapplication-example-cmdline4 \ - gapplication-example-dbushooks \ - gapplication-example-open \ - gdbus-example-export \ - gdbus-example-own-name \ - gdbus-example-peer \ - gdbus-example-proxy-subclass \ - gdbus-example-server \ - gdbus-example-subtree \ - gdbus-example-watch-name \ - gdbus-example-watch-proxy \ - gsubprocess-testprog \ - httpd \ - proxy \ - resolver \ - send-data \ - socket-server \ - $(NULL) - -test_extra_programs = \ - gdbus-connection-flush-helper \ - gdbus-testserver \ - $(NULL) - -dist_uninstalled_test_data += $(addprefix schema-tests/,$(schema_tests)) -schema_tests = \ - array-default-not-in-choices.gschema.xml \ - bad-choice.gschema.xml \ - bad-key.gschema.xml \ - bad-key2.gschema.xml \ - bad-key3.gschema.xml \ - bad-key4.gschema.xml \ - bad-type.gschema.xml \ - bare-alias.gschema.xml \ - choice-alias.gschema.xml \ - choice-bad.gschema.xml \ - choice-badtype.gschema.xml \ - choice-invalid-alias.gschema.xml \ - choice-missing-value.gschema.xml \ - choice-shadowed-alias.gschema.xml \ - choice-upside-down.gschema.xml \ - choice.gschema.xml \ - choices-wrong-type.gschema.xml \ - default-in-aliases.gschema.xml \ - default-not-in-choices.gschema.xml \ - default-out-of-range.gschema.xml \ - description-xmllang.gschema.xml \ - empty-key.gschema.xml \ - enum-with-aliases.gschema.xml \ - enum-with-bad-default.gschema.xml \ - enum-with-chained-alias.gschema.xml \ - enum-with-choice.gschema.xml \ - enum-with-invalid-alias.gschema.xml \ - enum-with-invalid-value.gschema.xml \ - enum-with-repeated-alias.gschema.xml \ - enum-with-repeated-nick.gschema.xml \ - enum-with-repeated-value.gschema.xml \ - enum-with-shadow-alias.gschema.xml \ - enum.gschema.xml \ - extend-and-shadow-indirect.gschema.xml \ - extend-and-shadow.gschema.xml \ - extend-missing.gschema.xml \ - extend-nonlist.gschema.xml \ - extend-self.gschema.xml \ - extend-wrong-list-indirect.gschema.xml \ - extend-wrong-list.gschema.xml \ - extending.gschema.xml \ - flags-aliased-default.gschema.xml \ - flags-bad-default.gschema.xml \ - flags-more-than-one-bit.gschema.xml \ - flags-with-enum-attr.gschema.xml \ - flags-with-enum-tag.gschema.xml \ - from-docs.gschema.xml \ - incomplete-list.gschema.xml \ - inherit-gettext-domain.gschema.xml \ - invalid-path.gschema.xml \ - key-in-list-indirect.gschema.xml \ - key-in-list.gschema.xml \ - list-of-missing.gschema.xml \ - missing-quotes.gschema.xml \ - no-default.gschema.xml \ - overflow.gschema.xml \ - override-missing.gschema.xml \ - override-range-error.gschema.xml \ - override-then-key.gschema.xml \ - override-twice.gschema.xml \ - override-type-error.gschema.xml \ - override.gschema.xml \ - range-badtype.gschema.xml \ - range-default-high.gschema.xml \ - range-default-low.gschema.xml \ - range-high-default.gschema.xml \ - range-low-default.gschema.xml \ - range-missing-max.gschema.xml \ - range-missing-min.gschema.xml \ - range-parse-error.gschema.xml \ - range-wrong-type.gschema.xml \ - range.gschema.xml \ - summary-xmllang.gschema.xml \ - summary-xmllang-and-attrs.gschema.xml \ - wrong-category.gschema.xml \ - $(NULL) - -test_programs += network-address -network_address_SOURCES = \ - network-address.c \ - mock-resolver.c \ - mock-resolver.h - -test_programs += thumbnail-verification -dist_test_data += $(thumbnail_data_files) -thumbnail_data_files = $(addprefix thumbnails/,$(thumbnail_tests)) -thumbnail_tests = \ - bad-header.png \ - empty-key.png \ - header-and-chunk-size.png \ - header-only.png \ - huge-chunk-size.png \ - mtime-zero.png \ - no-text-data.png \ - overlong-value.png \ - uri-mismatch.png \ - valid.png \ - valid-no-size.png \ - $(NULL) - -test_programs += tls-certificate -tls_certificate_SOURCES = \ - tls-certificate.c \ - gtesttlsbackend.c \ - gtesttlsbackend.h -dist_test_data += $(cert_data_files) -cert_data_files = $(addprefix cert-tests/,$(cert_tests)) -cert_tests = \ - cert1.pem \ - cert2.pem \ - cert3.pem \ - cert-crlf.pem \ - cert-key.pem \ - cert-list.pem \ - key8.pem \ - key8enc.pem \ - key-cert.pem \ - key.pem \ - key-crlf.pem \ - key_missing-footer.pem \ - key_missing-header.pem \ - nothing.pem \ - $(NULL) - -uninstalled_test_extra_programs += socket-client -socket_client_SOURCES = \ - socket-client.c \ - gtlsconsoleinteraction.c \ - gtlsconsoleinteraction.h -EXTRA_DIST += socket-common.c - -uninstalled_test_extra_programs += gdbus-daemon -gdbus_daemon_SOURCES = gdbus-daemon.c -nodist_gdbus_daemon_SOURCES = \ - gdbus-daemon-generated.c \ - gdbus-daemon-impl.c -CLEANFILES += gdbus-daemon-impl.c gdbus-daemon-generated.c gdbus-daemon-generated.h - -# With subdir-objects we need to create a link to the original -# file in the right directory, otherwise libtool will complain -# that it cannot find the wrapper file -gdbus-daemon-impl.c: $(top_srcdir)/gio/gdbusdaemon.c - $(AM_V_GEN) $(LN_S) $^ $@ - -# These files are only generated on Windows builds inside GIO, -# but we want them on non-Windows builds for the tests -gdbus-daemon-generated.h gdbus-daemon-generated.c: $(top_srcdir)/gio/dbus-daemon.xml $(GDBUS_PYTHON_DEPS) - $(AM_V_GEN) UNINSTALLED_GLIB_SRCDIR=$(top_srcdir) \ - UNINSTALLED_GLIB_BUILDDIR=$(top_builddir) \ - $(PYTHON) $(top_srcdir)/gio/gdbus-2.0/codegen/gdbus-codegen.in \ - --interface-prefix org. \ - --generate-c-code gdbus-daemon-generated \ - --c-namespace _G \ - $(top_srcdir)/gio/dbus-daemon.xml \ - $(NULL) - -# ----------------------------------------------------------------------------- -# Test programs buildable on UNIX only - -if OS_UNIX -test_programs += \ - file \ - gdbus-peer-object-manager \ - live-g-file \ - socket-address \ - stream-rw_all \ - unix-fd \ - unix-mounts \ - unix-streams \ - g-file-info-filesystem-readonly \ - trash \ - $(NULL) - -test_extra_programs += \ - basic-application \ - dbus-launch \ - $(NULL) - -if !OS_COCOA -# Uninstalled because of the check-for-executable logic in DesktopAppInfo unable to find the installed executable -uninstalled_test_programs += \ - appinfo \ - desktop-app-info \ - $(NULL) -endif - -home_desktop_files = \ - epiphany-weather-for-toronto-island-9c6a4e022b17686306243dada811d550d25eb1fb.desktop \ - eog.desktop - -usr_desktop_files = \ - baobab.desktop \ - cheese.desktop \ - dconf-editor.desktop \ - eog.desktop \ - evince-previewer.desktop \ - evince.desktop \ - file-roller.desktop \ - frobnicator.desktop \ - gcr-prompter.desktop \ - gcr-viewer.desktop \ - gedit.desktop \ - glade.desktop \ - gnome-contacts.desktop \ - gnome-font-viewer.desktop \ - gnome-music.desktop \ - gnome-terminal.desktop \ - gucharmap.desktop \ - kde4/dolphin.desktop \ - kde4/kate.desktop \ - kde4/konqbrowser.desktop \ - kde4/okular.desktop \ - mimeinfo.cache \ - nautilus-autorun-software.desktop \ - nautilus-classic.desktop \ - nautilus-connect-server.desktop \ - nautilus.desktop \ - org.gnome.clocks.desktop \ - totem.desktop \ - yelp.desktop - -dist_test_data += \ - $(addprefix desktop-files/usr/applications/,$(usr_desktop_files)) \ - $(addprefix desktop-files/home/applications/,$(home_desktop_files)) - -dist_test_data += \ - appinfo-test-actions.desktop \ - appinfo-test-static.desktop \ - file.c \ - org.gtk.test.dbusappinfo.desktop \ - x-content/image-dcf/DCIM/Camera/20130831_203925.jpg \ - x-content/image-dcf/DCIM/Camera/20130831_203928.jpg \ - x-content/unix-software/autorun.sh \ - x-content/win32-software/autorun.exe \ - $(NULL) - -test_extra_programs += \ - appinfo-test \ - $(NULL) - -uninstalled_test_extra_programs += \ - gdbus-example-unix-fd-client \ - $(NULL) - -if !OS_COCOA -test_extra_programs += apps -test_programs += mimeapps -endif - -appinfo-test-gnome.desktop: appinfo-test-gnome.desktop.in Makefile - $(AM_V_GEN)$(SED) \ - -e 's|[@]installed_tests_dir[@]|$(installed_testdir)|g' \ - $< > $@ -appinfo-test-notgnome.desktop: appinfo-test-notgnome.desktop.in Makefile - $(AM_V_GEN)$(SED) \ - -e 's|[@]installed_tests_dir[@]|$(installed_testdir)|g' \ - $< > $@ -appinfo-test.desktop: appinfo-test.desktop.in Makefile - $(AM_V_GEN)$(SED) \ - -e 's|[@]installed_tests_dir[@]|$(installed_testdir)|g' \ - $< > $@ -appinfo-test2.desktop: appinfo-test2.desktop.in Makefile - $(AM_V_GEN)$(SED) \ - -e 's|[@]installed_tests_dir[@]|$(installed_testdir)|g' \ - $< > $@ - -appinfo_desktop_templates = \ - appinfo-test-gnome.desktop.in \ - appinfo-test-notgnome.desktop.in \ - appinfo-test.desktop.in \ - appinfo-test2.desktop.in \ - $(NULL) -appinfo_desktop_files = $(appinfo_desktop_templates:.in=) - -EXTRA_DIST += $(appinfo_desktop_templates) -CLEANFILES += $(appinfo_desktop_files) -test_data += $(appinfo_desktop_files) - -uninstalled_test_programs += gsettings gschema-compile -gsettings_DEPENDENCIES = test.mo -CLEANFILES += test.mo de/LC_MESSAGES/test.mo keyfile/gsettings.store -gsettings_CFLAGS = $(AM_CFLAGS) -DSRCDIR=\"$(abs_srcdir)\" -test.mo: de.po - $(AM_V_GEN) $(MSGFMT) -o test.mo $(srcdir)/de.po; \ - $(MKDIR_P) de/LC_MESSAGES; \ - cp -f test.mo de/LC_MESSAGES -EXTRA_DIST += de.po -dist_uninstalled_test_data += \ - org.gtk.test.gschema.xml.orig \ - org.gtk.test.gschema.override.orig \ - org.gtk.schemasourcecheck.gschema.xml \ - testenum.h \ - enums.xml.template -# Generated while running the testcase itself... -CLEANFILES += \ - org.gtk.test.gschema.xml \ - org.gtk.test.gschema.override \ - org.gtk.test.enums.xml \ - gsettings.store \ - gschemas.compiled \ - schema-source/gschemas.compiled \ - schema-source-corrupt/gschemas.compiled \ - schema-source-empty/gschemas.compiled \ - $(NULL) - -test_programs += gdbus-connection-flush -gdbus_connection_flush_SOURCES = \ - gdbus-connection-flush.c \ - test-io-stream.c \ - test-io-stream.h \ - test-pipe-unix.c \ - test-pipe-unix.h - -test_programs += gdbus-non-socket -gdbus_non_socket_SOURCES = \ - gdbus-non-socket.c \ - gdbus-tests.c \ - gdbus-tests.h \ - test-io-stream.c \ - test-io-stream.h \ - test-pipe-unix.c \ - test-pipe-unix.h - -# These three are manual-run tests because they need a session bus but don't bring one up themselves -uninstalled_test_extra_programs += gdbus-example-objectmanager-client -gdbus_example_objectmanager_client_LDADD = gdbus-object-manager-example/libgdbus-example-objectmanager.la $(LDADD) - -uninstalled_test_extra_programs += gdbus-example-objectmanager-server -gdbus_example_objectmanager_server_LDADD = gdbus-object-manager-example/libgdbus-example-objectmanager.la $(LDADD) - -test_extra_programs += gsubprocess-testprog - -uninstalled_test_extra_programs += gdbus-test-fixture -gdbus_test_fixture_LDADD = gdbus-object-manager-example/libgdbus-example-objectmanager.la $(LDADD) - -# This is peer to peer so it doesn't need a session bus (so we can run it normally) -gdbus_peer_LDADD = gdbus-object-manager-example/libgdbus-example-objectmanager.la $(LDADD) - -# This test is currently unreliable -test_extra_programs += gdbus-overflow - -# ----------------------------------------------------------------------------- -# Test programs that need to bring up a session bus (requires dbus-daemon) - -if HAVE_DBUS_DAEMON -gdbus_sessionbus_sources = gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c - -test_programs += \ - actions \ - defaultvalue \ - gapplication \ - gdbus-auth \ - gdbus-bz627724 \ - gdbus-close-pending \ - gdbus-connection \ - gdbus-connection-loss \ - gdbus-connection-slow \ - gdbus-error \ - gdbus-exit-on-close \ - gdbus-export \ - gdbus-introspection \ - gdbus-names \ - gdbus-peer \ - gdbus-proxy \ - gdbus-proxy-threads \ - gdbus-proxy-unique-name \ - gdbus-proxy-well-known-name \ - gdbus-test-codegen \ - gdbus-test-codegen-old \ - gdbus-threading \ - gmenumodel \ - gnotification \ - $(NULL) - -if OS_UNIX -test_programs += gdbus-unix-addresses - -if !OS_COCOA -test_programs += dbus-appinfo -endif - -endif - -gdbus_proxy_threads_CFLAGS = $(AM_CFLAGS) $(DBUS1_CFLAGS) -actions_SOURCES = $(gdbus_sessionbus_sources) actions.c -dbus_appinfo_SOURCES = $(gdbus_sessionbus_sources) dbus-appinfo.c -gapplication_SOURCES = $(gdbus_sessionbus_sources) gapplication.c -gdbus_auth_SOURCES = $(gdbus_sessionbus_sources) gdbus-auth.c -gdbus_bz627724_SOURCES = $(gdbus_sessionbus_sources) gdbus-bz627724.c -gdbus_close_pending_SOURCES = $(gdbus_sessionbus_sources) gdbus-close-pending.c -gdbus_connection_SOURCES = $(gdbus_sessionbus_sources) gdbus-connection.c -gdbus_connection_loss_SOURCES = $(gdbus_sessionbus_sources) gdbus-connection-loss.c -gdbus_connection_slow_SOURCES = $(gdbus_sessionbus_sources) gdbus-connection-slow.c -gdbus_error_SOURCES = $(gdbus_sessionbus_sources) gdbus-error.c -gdbus_exit_on_close_SOURCES = $(gdbus_sessionbus_sources) gdbus-exit-on-close.c -gdbus_export_SOURCES = $(gdbus_sessionbus_sources) gdbus-export.c -gdbus_introspection_SOURCES = $(gdbus_sessionbus_sources) gdbus-introspection.c -gdbus_names_SOURCES = $(gdbus_sessionbus_sources) gdbus-names.c -gdbus_proxy_SOURCES = $(gdbus_sessionbus_sources) gdbus-proxy.c -gdbus_proxy_threads_SOURCES = $(gdbus_sessionbus_sources) gdbus-proxy-threads.c -gdbus_proxy_unique_name_SOURCES = $(gdbus_sessionbus_sources) gdbus-proxy-unique-name.c -gdbus_proxy_well_known_name_SOURCES = $(gdbus_sessionbus_sources) gdbus-proxy-well-known-name.c -gdbus_test_codegen_SOURCES = $(gdbus_sessionbus_sources) gdbus-test-codegen.c -nodist_gdbus_test_codegen_SOURCES = gdbus-test-codegen-generated.c gdbus-test-codegen-generated.h gdbus-test-codegen-generated-interface-info.c gdbus-test-codegen-generated-interface-info.h -gdbus_test_codegen_old_SOURCES = $(gdbus_sessionbus_sources) gdbus-test-codegen.c -nodist_gdbus_test_codegen_old_SOURCES = gdbus-test-codegen-generated.c gdbus-test-codegen-generated.h gdbus-test-codegen-generated-interface-info.c gdbus-test-codegen-generated-interface-info.h -gdbus_test_codegen_old_CPPFLAGS = $(AM_CPPFLAGS) -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36 -gdbus_threading_SOURCES = $(gdbus_sessionbus_sources) gdbus-threading.c -gmenumodel_SOURCES = $(gdbus_sessionbus_sources) gmenumodel.c -gnotification_SOURCES = $(gdbus_sessionbus_sources) gnotification.c gnotification-server.h gnotification-server.c - -BUILT_SOURCES += gdbus-test-codegen-generated.c gdbus-test-codegen-generated.h gdbus-test-codegen-generated-interface-info.c gdbus-test-codegen-generated-interface-info.h -gdbus-test-codegen.o: gdbus-test-codegen-generated.h gdbus-test-codegen-generated-interface-info.h -gdbus-test-codegen-generated.h: test-codegen.xml Makefile $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen - $(AM_V_GEN) UNINSTALLED_GLIB_SRCDIR=$(top_srcdir) \ - UNINSTALLED_GLIB_BUILDDIR=$(top_builddir) \ - $(PYTHON) $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen \ - --interface-prefix org.project. \ - --generate-c-code gdbus-test-codegen-generated \ - --c-generate-object-manager \ - --c-generate-autocleanup all \ - --c-namespace Foo_iGen \ - --generate-docbook gdbus-test-codegen-generated-doc \ - --annotate "org.project.Bar" Key1 Value1 \ - --annotate "org.project.Bar" org.gtk.GDBus.Internal Value2 \ - --annotate "org.project.Bar.HelloWorld()" Key3 Value3 \ - --annotate "org.project.Bar::TestSignal" Key4 Value4 \ - --annotate "org.project.Bar:ay" Key5 Value5 \ - --annotate "org.project.Bar.TestPrimitiveTypes()[val_int32]" Key6 Value6 \ - --annotate "org.project.Bar.TestPrimitiveTypes()[ret_uint32]" Key7 Value7 \ - --annotate "org.project.Bar::TestSignal[array_of_strings]" Key8 Value8 \ - $(srcdir)/test-codegen.xml \ - $(NULL) -gdbus-test-codegen-generated.c: gdbus-test-codegen-generated.h - @: # Generated as side-effect of .h -gdbus-test-codegen-generated-interface-info.h: test-codegen.xml Makefile $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen - $(AM_V_GEN) UNINSTALLED_GLIB_SRCDIR=$(top_srcdir) \ - UNINSTALLED_GLIB_BUILDDIR=$(top_builddir) \ - $(PYTHON) $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen \ - --interface-info-header \ - --annotate "org.project.Bar" Key1 Value1 \ - --annotate "org.project.Bar" org.gtk.GDBus.Internal Value2 \ - --annotate "org.project.Bar.HelloWorld()" Key3 Value3 \ - --annotate "org.project.Bar::TestSignal" Key4 Value4 \ - --annotate "org.project.Bar:ay" Key5 Value5 \ - --annotate "org.project.Bar.TestPrimitiveTypes()[val_int32]" Key6 Value6 \ - --annotate "org.project.Bar.TestPrimitiveTypes()[ret_uint32]" Key7 Value7 \ - --annotate "org.project.Bar::TestSignal[array_of_strings]" Key8 Value8 \ - --output $@ \ - $(srcdir)/test-codegen.xml \ - $(NULL) -gdbus-test-codegen-generated-interface-info.c: test-codegen.xml Makefile $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen - $(AM_V_GEN) UNINSTALLED_GLIB_SRCDIR=$(top_srcdir) \ - UNINSTALLED_GLIB_BUILDDIR=$(top_builddir) \ - $(PYTHON) $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen \ - --interface-info-body \ - --annotate "org.project.Bar" Key1 Value1 \ - --annotate "org.project.Bar" org.gtk.GDBus.Internal Value2 \ - --annotate "org.project.Bar.HelloWorld()" Key3 Value3 \ - --annotate "org.project.Bar::TestSignal" Key4 Value4 \ - --annotate "org.project.Bar:ay" Key5 Value5 \ - --annotate "org.project.Bar.TestPrimitiveTypes()[val_int32]" Key6 Value6 \ - --annotate "org.project.Bar.TestPrimitiveTypes()[ret_uint32]" Key7 Value7 \ - --annotate "org.project.Bar::TestSignal[array_of_strings]" Key8 Value8 \ - --output $@ \ - $(srcdir)/test-codegen.xml \ - $(NULL) - -EXTRA_DIST += test-codegen.xml -CLEANFILES += gdbus-test-codegen-generated.[ch] gdbus-test-codegen-generated-doc-*.xml gdbus-test-codegen-generated-interface-info.[ch] -endif # OS_UNIX -endif # HAVE_DBUS_DAEMON - -tls_interaction_SOURCES = tls-interaction.c gtesttlsbackend.c gtesttlsbackend.h -tls_database_SOURCES = tls-database.c gtesttlsbackend.c gtesttlsbackend.h - -# ----------------------------------------------------------------------------- - -if OS_WIN32 -test_programs += win32-streams -endif - -if PLATFORM_WIN32 -no_undefined = -no-undefined -endif - -if HAVE_DBUS1 -test_programs += gdbus-serialization -gdbus_serialization_CFLAGS = $(AM_CFLAGS) $(DBUS1_CFLAGS) -gdbus_serialization_LDADD = $(LDADD) $(DBUS1_LIBS) -gdbus_serialization_SOURCES = \ - gdbus-serialization.c \ - gdbus-tests.h \ - gdbus-tests.c -endif - -if HAVE_GCC -test_programs += \ - autoptr \ - $(NULL) -endif - -# ----------------------------------------------------------------------------- -# The resources test is a bit more complicated, and we cannot build it when -# cross-compiling GIO because it requires running a binary... - -if !CROSS_COMPILING -test_programs += resources -resources_SOURCES = resources.c -nodist_resources_SOURCES = test_resources.c test_resources2.c test_resources2.h -resources_DEPENDENCIES = test.gresource - -test_ltlibraries += libresourceplugin.la -libresourceplugin_la_SOURCES = resourceplugin.c -nodist_libresourceplugin_la_SOURCES = plugin_resources.c -libresourceplugin_la_LDFLAGS = -avoid-version -module -export-dynamic $(no_undefined) -libresourceplugin_la_LIBADD = $(LDADD) - -test_data += test.gresource - -# libtool contains a bug whereby the created .la file doesn't contain the correct dlname='' in the case that -# you're building a library but not installing it. This is apparently because the only considered use for an -# uninstalled library is as a convenience library for linking (despite the fact that we give -module). The lack -# of dlname='' in the .la trips up libltdl and GModule as well. We can trick libtool into believing that we -# will install the module by giving it a bogus -rpath for the uninstalled cases. -# -# See http://lists.gnu.org/archive/html/bug-libtool/2013-05/msg00009.html -if !ENABLE_INSTALLED_TESTS -libresourceplugin_la_LDFLAGS += -rpath / -endif - -glib_compile_resources=$(top_builddir)/gio/glib-compile-resources - -test-generated.txt: test1.txt - $(AM_V_GEN) echo "Generated" > $@ && \ - cat $< >> $@ - -gresource-big-test.txt: gen-big-test-resource.py - $(AM_V_GEN) $(PYTHON) $< $@ - -resources.o: test_resources2.h -test_resources.c: test2.gresource.xml Makefile $(shell $(glib_compile_resources) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/test2.gresource.xml) - $(AM_V_GEN) $(glib_compile_resources) --target=$@ --sourcedir=. --sourcedir=$(srcdir) --generate-source --c-name _g_test1 $< - -test_resources2.h test_resources2.c: test3.gresource.xml Makefile $(shell $(glib_compile_resources) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/test3.gresource.xml) - $(AM_V_GEN) $(glib_compile_resources) --target=$@ --sourcedir=$(srcdir) --generate --c-name _g_test2 --manual-register $< - -plugin_resources.c: test4.gresource.xml Makefile $(shell $(glib_compile_resources) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/test4.gresource.xml) - $(AM_V_GEN) $(glib_compile_resources) --target=$@ --sourcedir=$(srcdir) --generate-source --c-name _g_plugin $< - -test.gresource: test.gresource.xml Makefile $(shell $(glib_compile_resources) --sourcedir=. --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/test.gresource.xml) - $(AM_V_GEN) $(glib_compile_resources) --target=$@ --sourcedir=. --sourcedir=$(srcdir) $< - -EXTRA_DIST += test.gresource.xml test1.txt test2.gresource.xml test2.txt test3.gresource.xml test3.txt test4.gresource.xml test5.gresource.xml gen-big-test-resource.py -CLEANFILES += test-generated.txt test_resources.c test_resources2.[ch] plugin_resources.c test.gresource gresource-big-test.txt - -if OS_LINUX -test5.gresource: test5.gresource.xml Makefile $(shell $(glib_compile_resources) --sourcedir=. --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/test5.gresource.xml) - $(AM_V_GEN) $(glib_compile_resources) --target=$@ --sourcedir=. --sourcedir=$(srcdir) $< -test_resources_binary.c: test5.gresource.xml Makefile $(shell $(glib_compile_resources) --sourcedir=. --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/test5.gresource.xml) - $(AM_V_GEN) $(glib_compile_resources) --target=$@ --sourcedir=$(srcdir) --generate-source --c-name _g_binary_test1 $< -test_resources_binary_data.o: test5.gresource - $(AM_V_GEN) ld -r -b binary $< -o $@ -test_resources_binary_data2.o: test_resources_binary.o - $(AM_V_GEN) objcopy --add-symbol _g_binary_test1_resource_data=.data:0 -N _binary_test5_gresource_start -N _binary_test5_gresource_size -N _binary_test5_gresource_end $< $@ -nodist_resources_SOURCES += test_resources_binary_data2.o test_resources_binary.c -endif - -endif # !CROSS_COMPILING - -BUILT_SOURCES += giotypefuncs.inc - -giotypefuncs.inc: Makefile - $(AM_V_GEN) echo '#include <gio/gio.h>' > xgen-giosrc.c && \ - echo "G_GNUC_BEGIN_IGNORE_DEPRECATIONS" > xgen-gio && \ - ${CPP} $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) xgen-giosrc.c | \ - $(GREP) -o '\bg_[A-Za-z0-9_]*_get_type\b' | \ - $(GREP) -v 'g_io_extension_get_type\|g_variant_get_type' | \ - LC_ALL=C sort | uniq | \ - $(SED) -e 's/^/*tp++ = /' -e 's/$$/ ();/' >> xgen-gio && \ - cp xgen-gio $@ # && rm -f xgen-gio xgen-giosrc.c - -CLEANFILES += xgen-giosrc.c xgen-gio giotypefuncs.inc - -if ENABLE_INSTALLED_TESTS -if OS_UNIX -install-data-hook: - $(AM_V_at) chmod a+x $(DESTDIR)$(installed_testdir)/x-content/win32-software/autorun.exe -endif -endif diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index dd41af842..ed2f89111 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -155,7 +155,7 @@ test_default (void) static void test_fallback (void) { - GAppInfo *info1, *info2, *app; + GAppInfo *info1, *info2, *app = NULL; GList *apps, *recomm, *fallback, *list, *l, *m; GError *error = NULL; gint old_length; @@ -188,6 +188,7 @@ test_fallback (void) if (g_app_info_equal (info1, app)) break; } + g_assert_nonnull (app); g_assert_true (g_app_info_equal (info1, app)); /* and that Test2 is among the fallback apps */ diff --git a/gio/tests/file.c b/gio/tests/file.c index d2f147419..658d17549 100644 --- a/gio/tests/file.c +++ b/gio/tests/file.c @@ -1162,6 +1162,561 @@ test_load_bytes_async (void) g_main_loop_unref (data.main_loop); } +static void +test_writev_helper (GOutputVector *vectors, + gsize n_vectors, + gboolean use_bytes_written, + const guint8 *expected_contents, + gsize expected_length) +{ + GFile *file; + GFileIOStream *iostream = NULL; + GOutputStream *ostream; + GError *error = NULL; + gsize bytes_written = 0; + gboolean res; + guint8 *contents; + gsize length; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + res = g_output_stream_writev_all (ostream, vectors, n_vectors, use_bytes_written ? &bytes_written : NULL, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + if (use_bytes_written) + g_assert_cmpuint (bytes_written, ==, expected_length); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + g_assert_cmpmem (contents, length, expected_contents, expected_length); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +/* Test that writev() on local file output streams works on a non-empty vector */ +static void +test_writev (void) +{ + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, buffer, sizeof buffer); +} + +/* Test that writev() on local file output streams works on a non-empty vector without returning bytes_written */ +static void +test_writev_no_bytes_written (void) +{ + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + test_writev_helper (vectors, G_N_ELEMENTS (vectors), FALSE, buffer, sizeof buffer); +} + +/* Test that writev() on local file output streams works on 0 vectors */ +static void +test_writev_no_vectors (void) +{ + test_writev_helper (NULL, 0, TRUE, NULL, 0); +} + +/* Test that writev() on local file output streams works on empty vectors */ +static void +test_writev_empty_vectors (void) +{ + GOutputVector vectors[3]; + + vectors[0].buffer = NULL; + vectors[0].size = 0; + vectors[1].buffer = NULL; + vectors[1].size = 0; + vectors[2].buffer = NULL; + vectors[2].size = 0; + + test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, NULL, 0); +} + +/* Test that writev() fails if the sum of sizes in the vector is too big */ +static void +test_writev_too_big_vectors (void) +{ + GFile *file; + GFileIOStream *iostream = NULL; + GOutputStream *ostream; + GError *error = NULL; + gsize bytes_written = 0; + gboolean res; + guint8 *contents; + gsize length; + GOutputVector vectors[3]; + + vectors[0].buffer = (void*) 1; + vectors[0].size = G_MAXSIZE / 2; + + vectors[1].buffer = (void*) 1; + vectors[1].size = G_MAXSIZE / 2; + + vectors[2].buffer = (void*) 1; + vectors[2].size = G_MAXSIZE / 2; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + res = g_output_stream_writev_all (ostream, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert_cmpuint (bytes_written, ==, 0); + g_assert_false (res); + g_clear_error (&error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + g_assert_cmpmem (contents, length, NULL, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +typedef struct +{ + gsize bytes_written; + GOutputVector *vectors; + gsize n_vectors; + GError *error; + gboolean done; +} WritevAsyncData; + +static void +test_writev_async_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GOutputStream *ostream = G_OUTPUT_STREAM (object); + WritevAsyncData *data = user_data; + GError *error = NULL; + gsize bytes_written; + gboolean res; + + res = g_output_stream_writev_finish (ostream, result, &bytes_written, &error); + g_assert_true (res); + g_assert_no_error (error); + data->bytes_written += bytes_written; + + /* skip vectors that have been written in full */ + while (data->n_vectors > 0 && bytes_written >= data->vectors[0].size) + { + bytes_written -= data->vectors[0].size; + ++data->vectors; + --data->n_vectors; + } + /* skip partially written vector data */ + if (bytes_written > 0 && data->n_vectors > 0) + { + data->vectors[0].size -= bytes_written; + data->vectors[0].buffer = ((guint8 *) data->vectors[0].buffer) + bytes_written; + } + + if (data->n_vectors > 0) + g_output_stream_writev_async (ostream, data->vectors, data->n_vectors, 0, NULL, test_writev_async_cb, &data); +} + +/* Test that writev_async() on local file output streams works on a non-empty vector */ +static void +test_writev_async (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + data.vectors = vectors; + data.n_vectors = G_N_ELEMENTS (vectors); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_async (ostream, data.vectors, data.n_vectors, 0, NULL, test_writev_async_cb, &data); + + while (data.n_vectors > 0) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, sizeof buffer); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + g_assert_cmpmem (contents, length, buffer, sizeof buffer); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +static void +test_writev_all_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GOutputStream *ostream = G_OUTPUT_STREAM (object); + WritevAsyncData *data = user_data; + + g_output_stream_writev_all_finish (ostream, result, &data->bytes_written, &data->error); + data->done = TRUE; +} + +/* Test that writev_async_all() on local file output streams works on a non-empty vector */ +static void +test_writev_async_all (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputStream *ostream; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, sizeof buffer); + g_assert_no_error (data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + g_assert_cmpmem (contents, length, buffer, sizeof buffer); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +/* Test that writev_async_all() on local file output streams handles cancellation correctly */ +static void +test_writev_async_all_cancellation (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + GCancellable *cancellable; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + cancellable = g_cancellable_new (); + g_cancellable_cancel (cancellable); + + g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, cancellable, test_writev_all_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, 0); + g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_error (&data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (length, ==, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); + g_object_unref (cancellable); +} + +/* Test that writev_async_all() with empty vectors is handled correctly */ +static void +test_writev_async_all_empty_vectors (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputVector vectors[3]; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + vectors[0].buffer = NULL; + vectors[0].size = 0; + + vectors[1].buffer = NULL; + vectors[1].size = 0; + + vectors[2].buffer = NULL; + vectors[2].size = 0; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, 0); + g_assert_no_error (data.error); + g_clear_error (&data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (length, ==, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +/* Test that writev_async_all() with no vectors is handled correctly */ +static void +test_writev_async_all_no_vectors (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_all_async (ostream, NULL, 0, 0, NULL, test_writev_all_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, 0); + g_assert_no_error (data.error); + g_clear_error (&data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (length, ==, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +/* Test that writev_async_all() with too big vectors is handled correctly */ +static void +test_writev_async_all_too_big_vectors (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputVector vectors[3]; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + vectors[0].buffer = (void*) 1; + vectors[0].size = G_MAXSIZE / 2; + + vectors[1].buffer = (void*) 1; + vectors[1].size = G_MAXSIZE / 2; + + vectors[2].buffer = (void*) 1; + vectors[2].size = G_MAXSIZE / 2; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, 0); + g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_clear_error (&data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (length, ==, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + int main (int argc, char *argv[]) { @@ -1190,6 +1745,17 @@ main (int argc, char *argv[]) g_test_add_func ("/file/measure-async", test_measure_async); g_test_add_func ("/file/load-bytes", test_load_bytes); g_test_add_func ("/file/load-bytes-async", test_load_bytes_async); + g_test_add_func ("/file/writev", test_writev); + g_test_add_func ("/file/writev/no-bytes-written", test_writev_no_bytes_written); + g_test_add_func ("/file/writev/no-vectors", test_writev_no_vectors); + g_test_add_func ("/file/writev/empty-vectors", test_writev_empty_vectors); + g_test_add_func ("/file/writev/too-big-vectors", test_writev_too_big_vectors); + g_test_add_func ("/file/writev/async", test_writev_async); + g_test_add_func ("/file/writev/async_all", test_writev_async_all); + g_test_add_func ("/file/writev/async_all-empty-vectors", test_writev_async_all_empty_vectors); + g_test_add_func ("/file/writev/async_all-no-vectors", test_writev_async_all_no_vectors); + g_test_add_func ("/file/writev/async_all-to-big-vectors", test_writev_async_all_too_big_vectors); + g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation); return g_test_run (); } diff --git a/gio/tests/gdbus-object-manager-example/Makefile.am b/gio/tests/gdbus-object-manager-example/Makefile.am deleted file mode 100644 index 735fddcc6..000000000 --- a/gio/tests/gdbus-object-manager-example/Makefile.am +++ /dev/null @@ -1,49 +0,0 @@ -include $(top_srcdir)/glib.mk - -AM_CPPFLAGS = -g $(gio_INCLUDES) $(GLIB_DEBUG_FLAGS) -I$(top_builddir)/gio -I$(top_srcdir)/gio - -# ------------------------------------------------------------------------ - -GDBUS_GENERATED = \ - objectmanager-gen.h \ - objectmanager-gen.c \ - objectmanager-gen-org.gtk.GDBus.Example.ObjectManager.Animal.xml \ - objectmanager-gen-org.gtk.GDBus.Example.ObjectManager.Cat.xml \ - $(NULL) - -$(GDBUS_GENERATED) : gdbus-example-objectmanager.xml Makefile $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen - $(AM_V_GEN) UNINSTALLED_GLIB_SRCDIR=$(top_srcdir) \ - UNINSTALLED_GLIB_BUILDDIR=$(top_builddir) \ - $(PYTHON) $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen \ - --interface-prefix org.gtk.GDBus.Example.ObjectManager. \ - --c-namespace Example \ - --c-generate-object-manager \ - --generate-c-code objectmanager-gen \ - --generate-docbook objectmanager-gen \ - $< \ - $(NULL) - -test_ltlibraries = libgdbus-example-objectmanager.la - -if ENABLE_GTK_DOC -# The docs pull these in, so we need them even if not doing 'make check' -BUILT_SOURCES += $(GDBUS_GENERATED) -noinst_LTLIBRARIES += libgdbus-example-objectmanager.la -endif - -nodist_libgdbus_example_objectmanager_la_SOURCES = \ - objectmanager-gen.h \ - objectmanager-gen.c - -libgdbus_example_objectmanager_la_LIBADD = \ - $(top_builddir)/glib/libglib-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/gio/libgio-2.0.la \ - $(NULL) - -EXTRA_DIST += gdbus-example-objectmanager.xml - -CLEANFILES += $(GDBUS_GENERATED) - -check-TESTS: diff --git a/gio/tests/gdbus-proxy.c b/gio/tests/gdbus-proxy.c index 8a2c324a2..6e092f42b 100644 --- a/gio/tests/gdbus-proxy.c +++ b/gio/tests/gdbus-proxy.c @@ -766,6 +766,7 @@ test_proxy (void) GDBusProxy *proxy; GDBusConnection *connection; GError *error; + gchar *owner; error = NULL; connection = g_bus_get_sync (G_BUS_TYPE_SESSION, @@ -783,7 +784,7 @@ test_proxy (void) &error); g_assert_no_error (error); - /* this is safe; testserver will exit once the bus goes away */ + /* this is safe; we explicitly kill the service later on */ g_assert (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver", NULL), NULL)); _g_assert_property_notify (proxy, "g-name-owner"); @@ -794,8 +795,15 @@ test_proxy (void) test_signals (proxy); test_expected_interface (proxy); - g_object_unref (proxy); kill_test_service (connection); + + _g_assert_property_notify (proxy, "g-name-owner"); + + owner = g_dbus_proxy_get_name_owner (proxy); + g_assert_null (owner); + g_free (owner); + + g_object_unref (proxy); g_object_unref (connection); } @@ -808,11 +816,19 @@ proxy_ready (GObject *source, { GDBusProxy *proxy; GError *error; + gchar *owner; error = NULL; proxy = g_dbus_proxy_new_for_bus_finish (result, &error); g_assert_no_error (error); + owner = g_dbus_proxy_get_name_owner (proxy); + g_assert_null (owner); + g_free (owner); + + /* this is safe; we explicitly kill the service later on */ + g_assert (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver", NULL), NULL)); + _g_assert_property_notify (proxy, "g-name-owner"); test_basic (proxy); @@ -847,9 +863,6 @@ test_async (void) proxy_ready, NULL); - /* this is safe; testserver will exit once the bus goes away */ - g_assert (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver", NULL), NULL)); - id = g_timeout_add (10000, fail_test, NULL); g_main_loop_run (loop); diff --git a/gio/tests/gdbus-test-codegen.c b/gio/tests/gdbus-test-codegen.c index e212e98cc..8db555781 100644 --- a/gio/tests/gdbus-test-codegen.c +++ b/gio/tests/gdbus-test-codegen.c @@ -56,7 +56,7 @@ test_annotations (void) iface = foo_igen_bar_interface_info (); g_assert (iface != NULL); - /* see Makefile.am for where these annotations are injected */ + /* see meson.build for where these annotations are injected */ g_assert_cmpint (count_annotations (iface->annotations), ==, 1); g_assert_cmpstr (g_dbus_annotation_info_lookup (iface->annotations, "Key1"), ==, "Value1"); diff --git a/gio/tests/glistmodel.c b/gio/tests/glistmodel.c index 533e2e47d..2fef4ccbe 100644 --- a/gio/tests/glistmodel.c +++ b/gio/tests/glistmodel.c @@ -778,6 +778,34 @@ test_store_signal_items_changed (void) g_object_unref (store); } +/* Due to an overflow in the list store last-iter optimization, + * the sequence 'lookup 0; lookup MAXUINT' was returning the + * same item twice, and not NULL for the second lookup. + * See #1639. + */ +static void +test_store_past_end (void) +{ + GListStore *store; + GListModel *model; + GSimpleAction *item; + + store = g_list_store_new (G_TYPE_SIMPLE_ACTION); + model = G_LIST_MODEL (store); + + item = g_simple_action_new ("2", NULL); + g_list_store_append (store, item); + g_object_unref (item); + + g_assert_cmpint (g_list_model_get_n_items (model), ==, 1); + item = g_list_model_get_item (model, 0); + g_assert_nonnull (item); + item = g_list_model_get_item (model, G_MAXUINT); + g_assert_null (item); + + g_object_unref (store); +} + int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); @@ -809,6 +837,7 @@ int main (int argc, char *argv[]) test_store_get_item_cache); g_test_add_func ("/glistmodel/store/items-changed", test_store_signal_items_changed); + g_test_add_func ("/glistmodel/store/past-end", test_store_past_end); return g_test_run (); } diff --git a/gio/tests/gschema-compile.c b/gio/tests/gschema-compile.c index 81ded83fa..8dc4985b8 100644 --- a/gio/tests/gschema-compile.c +++ b/gio/tests/gschema-compile.c @@ -11,11 +11,6 @@ typedef struct { const gchar *err; } SchemaTest; -/* Meson build defines this, autotools build does not */ -#ifndef GLIB_COMPILE_SCHEMAS -#define GLIB_COMPILE_SCHEMAS "../glib-compile-schemas" -#endif - static void test_schema_do_compile (gpointer data) { @@ -23,7 +18,7 @@ test_schema_do_compile (gpointer data) gchar *filename = g_strconcat (test->name, ".gschema.xml", NULL); gchar *path = g_test_build_filename (G_TEST_DIST, "schema-tests", filename, NULL); const gchar *argv[] = { - GLIB_COMPILE_SCHEMAS, + GLIB_COMPILE_SCHEMAS, /* defined in meson.build */ "--strict", "--dry-run", "--schema-file", path, diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c index 852a8b710..fb19e5156 100644 --- a/gio/tests/gsettings.c +++ b/gio/tests/gsettings.c @@ -1770,6 +1770,23 @@ test_keyfile (void) g_assert_cmpstr (str, ==, "howdy"); g_free (str); + /* Now check setting a string without quotes */ + called = FALSE; + g_signal_connect (settings, "changed::greeting", G_CALLBACK (key_changed_cb), &called); + + g_key_file_set_string (keyfile, "tests", "greeting", "he\"l🤗uń"); + g_free (data); + data = g_key_file_to_data (keyfile, &len, NULL); + g_file_set_contents ("keyfile/gsettings.store", data, len, &error); + g_assert_no_error (error); + while (!called) + g_main_context_iteration (NULL, FALSE); + g_signal_handlers_disconnect_by_func (settings, key_changed_cb, &called); + + str = g_settings_get_string (settings, "greeting"); + g_assert_cmpstr (str, ==, "he\"l🤗uń"); + g_free (str); + g_settings_set (settings, "farewell", "s", "cheerio"); called = FALSE; @@ -2798,12 +2815,8 @@ main (int argc, char *argv[]) if (!backend_set) g_setenv ("GSETTINGS_BACKEND", "memory", TRUE); -/* Meson build defines this, autotools build does not */ -#ifndef GLIB_MKENUMS -#define GLIB_MKENUMS "../../gobject/glib-mkenums" -#endif - g_remove ("org.gtk.test.enums.xml"); + /* #GLIB_MKENUMS is defined in meson.build */ g_assert (g_spawn_command_line_sync (GLIB_MKENUMS " " "--template " SRCDIR "/enums.xml.template " SRCDIR "/testenum.h", @@ -2820,12 +2833,8 @@ main (int argc, char *argv[]) g_assert (g_file_set_contents ("org.gtk.test.gschema.override", override_text, -1, NULL)); g_free (override_text); -/* Meson build defines this, autotools build does not */ -#ifndef GLIB_COMPILE_SCHEMAS -#define GLIB_COMPILE_SCHEMAS "../glib-compile-schemas" -#endif - g_remove ("gschemas.compiled"); + /* #GLIB_COMPILE_SCHEMAS is defined in meson.build */ g_assert (g_spawn_command_line_sync (GLIB_COMPILE_SCHEMAS " --targetdir=. " "--schema-file=org.gtk.test.enums.xml " "--schema-file=org.gtk.test.gschema.xml " diff --git a/gio/tests/memory-output-stream.c b/gio/tests/memory-output-stream.c index 4c85993e4..72da6263d 100644 --- a/gio/tests/memory-output-stream.c +++ b/gio/tests/memory-output-stream.c @@ -300,6 +300,94 @@ test_write_bytes (void) g_bytes_unref (bytes2); } +/* Test that writev() works on #GMemoryOutputStream with a non-empty set of vectors. This + * covers the default writev() implementation around write(). */ +static void +test_writev (void) +{ + GOutputStream *mo; + GError *error = NULL; + gboolean res; + gsize bytes_written; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + guint8 *output_buffer; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + vectors[0].size; + vectors[1].size = 12; + + vectors[2].buffer = buffer + vectors[0].size + vectors[1].size; + vectors[2].size = 3; + + mo = (GOutputStream*) g_object_new (G_TYPE_MEMORY_OUTPUT_STREAM, + "realloc-function", g_realloc, + "destroy-function", g_free, + NULL); + res = g_output_stream_writev_all (mo, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (bytes_written, ==, sizeof buffer); + + g_output_stream_close (mo, NULL, &error); + g_assert_no_error (error); + + g_assert_cmpuint (g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mo)), ==, sizeof buffer); + output_buffer = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mo)); + g_assert_cmpmem (output_buffer, sizeof buffer, buffer, sizeof buffer); + + g_object_unref (mo); +} + +/* Test that writev_nonblocking() works on #GMemoryOutputStream with a non-empty set of vectors. This + * covers the default writev_nonblocking() implementation around write_nonblocking(). */ +static void +test_writev_nonblocking (void) +{ + GOutputStream *mo; + GError *error = NULL; + gboolean res; + gsize bytes_written; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + guint8 *output_buffer; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + vectors[0].size; + vectors[1].size = 12; + + vectors[2].buffer = buffer + vectors[0].size + vectors[1].size; + vectors[2].size = 3; + + mo = (GOutputStream*) g_object_new (G_TYPE_MEMORY_OUTPUT_STREAM, + "realloc-function", g_realloc, + "destroy-function", g_free, + NULL); + res = g_pollable_output_stream_writev_nonblocking (G_POLLABLE_OUTPUT_STREAM (mo), + vectors, G_N_ELEMENTS (vectors), + &bytes_written, NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (res, ==, G_POLLABLE_RETURN_OK); + g_assert_cmpuint (bytes_written, ==, sizeof buffer); + + g_output_stream_close (mo, NULL, &error); + g_assert_no_error (error); + + g_assert_cmpuint (g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mo)), ==, sizeof buffer); + output_buffer = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mo)); + g_assert_cmpmem (output_buffer, sizeof buffer, buffer, sizeof buffer); + + g_object_unref (mo); +} + static void test_steal_as_bytes (void) { @@ -350,6 +438,8 @@ main (int argc, g_test_add_func ("/memory-output-stream/get-data-size", test_data_size); g_test_add_func ("/memory-output-stream/properties", test_properties); g_test_add_func ("/memory-output-stream/write-bytes", test_write_bytes); + g_test_add_func ("/memory-output-stream/writev", test_writev); + g_test_add_func ("/memory-output-stream/writev_nonblocking", test_writev_nonblocking); g_test_add_func ("/memory-output-stream/steal_as_bytes", test_steal_as_bytes); return g_test_run(); diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 5bbc07176..8bf555cf2 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -55,7 +55,7 @@ gio_tests = { 'memory-output-stream' : {}, 'monitor' : {}, 'mount-operation' : {}, - 'network-address' : {'extra_sources': ['mock-resolver.c']}, + 'network-address' : {'extra_sources': ['mock-resolver.c'], 'suite': ['flaky']}, 'network-monitor' : {}, 'network-monitor-race' : {}, 'permission' : {}, @@ -73,7 +73,7 @@ gio_tests = { 'vfs' : {}, 'volumemonitor' : {}, 'glistmodel' : {}, - 'testfilemonitor' : {'suite' : ['slow']}, + 'testfilemonitor' : {'suite' : ['slow', 'flaky']}, 'thumbnail-verification' : {}, 'tls-certificate' : {'extra_sources' : ['gtesttlsbackend.c']}, 'tls-interaction' : {'extra_sources' : ['gtesttlsbackend.c']}, @@ -144,7 +144,8 @@ if host_machine.system() != 'windows' ], 'env' : { 'LD_PRELOAD': '@0@/slow-connect-preload.so'.format(meson.current_build_dir()) - } + }, + 'suite': ['flaky'], }, 'gschema-compile' : {'install' : false}, 'trash' : {}, @@ -236,10 +237,10 @@ if host_machine.system() != 'windows' 'extra_sources' : extra_sources, 'suite' : ['slow'], }, - 'gdbus-auth' : {'extra_sources' : extra_sources}, - 'gdbus-bz627724' : {'extra_sources' : extra_sources}, + 'gdbus-auth' : {'extra_sources' : extra_sources, 'suite': ['flaky']}, + 'gdbus-bz627724' : {'extra_sources' : extra_sources, 'suite': ['flaky']}, 'gdbus-close-pending' : {'extra_sources' : extra_sources}, - 'gdbus-connection' : {'extra_sources' : extra_sources}, + 'gdbus-connection' : {'extra_sources' : extra_sources, 'suite': ['flaky']}, 'gdbus-connection-loss' : {'extra_sources' : extra_sources}, 'gdbus-connection-slow' : {'extra_sources' : extra_sources}, 'gdbus-error' : {'extra_sources' : extra_sources}, @@ -262,7 +263,7 @@ if host_machine.system() != 'windows' }, 'gdbus-threading' : { 'extra_sources' : extra_sources, - 'suite' : ['slow'], + 'suite' : ['slow', 'flaky'], }, 'gmenumodel' : { 'extra_sources' : extra_sources, diff --git a/gio/tests/modules/Makefile.am b/gio/tests/modules/Makefile.am deleted file mode 100644 index c301b6c47..000000000 --- a/gio/tests/modules/Makefile.am +++ /dev/null @@ -1,41 +0,0 @@ -NULL = - -LDADD = \ - $(top_builddir)/gio/libgio-2.0.la \ - $(top_builddir)/gobject/libgobject-2.0.la \ - $(top_builddir)/gmodule/libgmodule-2.0.la \ - $(top_builddir)/glib/libglib-2.0.la \ - $(NULL) - -AM_CPPFLAGS = \ - $(gio_INCLUDES) $(GLIB_DEBUG_FLAGS) \ - -I$(top_builddir)/gio \ - -I$(top_srcdir)/gio \ - $(NULL) - -modules = \ - libtestmodulea.la \ - libtestmoduleb.la \ - $(NULL) - -if ENABLE_ALWAYS_BUILD_TESTS -noinst_LTLIBRARIES = $(modules) -else -check_LTLIBRARIES = $(modules) -endif - -if ENABLE_INSTALLED_TESTS -testmoduledir = $(installed_testdir)/modules -testmodule_LTLIBRARIES = $(modules) -else -# See comment in Makefile.am one level up -rpath_hack = -rpath / -endif - -libtestmodulea_la_SOURCES = test-module-a.c symbol-visibility.h -libtestmodulea_la_LIBADD = $(LDADD) -libtestmodulea_la_LDFLAGS = $(LDFLAGS) -module -no-undefined -avoid-version $(rpath_hack) - -libtestmoduleb_la_SOURCES = test-module-b.c symbol-visibility.h -libtestmoduleb_la_LIBADD = $(LDADD) -libtestmoduleb_la_LDFLAGS =$(LDFLAGS) -module -no-undefined -avoid-version $(rpath_hack) diff --git a/gio/tests/resources.c b/gio/tests/resources.c index 719624514..6e9c7c5e6 100644 --- a/gio/tests/resources.c +++ b/gio/tests/resources.c @@ -679,7 +679,6 @@ test_uri_query_info (void) g_resources_register (resource); file = g_file_new_for_uri ("resource://" "/a_prefix/test2-alias.txt"); - info = g_file_query_info (file, "*", 0, NULL, &error); g_assert_no_error (error); @@ -893,6 +892,37 @@ test_resource_64k (void) g_bytes_unref (data); } +/* Check that g_resources_get_info() respects G_RESOURCE_OVERLAYS */ +static void +test_overlay (void) +{ + if (g_test_subprocess ()) + { + GError *error = NULL; + gboolean res; + gsize size; + char *overlay; + char *path; + + path = g_test_build_filename (G_TEST_DIST, "test1.overlay", NULL); + overlay = g_strconcat ("/auto_loaded/test1.txt=", path, NULL); + + g_setenv ("G_RESOURCE_OVERLAYS", overlay, TRUE); + res = g_resources_get_info ("/auto_loaded/test1.txt", 0, &size, NULL, &error); + g_assert_true (res); + g_assert_no_error (error); + /* test1.txt is 6 bytes, test1.overlay is 23 */ + g_assert_cmpint (size, ==, 23); + + g_free (overlay); + g_free (path); + + return; + } + g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_INHERIT_STDERR); + g_test_trap_assert_passed (); +} + int main (int argc, char *argv[]) @@ -919,6 +949,7 @@ main (int argc, g_test_add_func ("/resource/uri/query-info", test_uri_query_info); g_test_add_func ("/resource/uri/file", test_uri_file); g_test_add_func ("/resource/64k", test_resource_64k); + g_test_add_func ("/resource/overlay", test_overlay); return g_test_run(); } diff --git a/gio/tests/services/Makefile.am b/gio/tests/services/Makefile.am deleted file mode 100644 index 7ab7864c3..000000000 --- a/gio/tests/services/Makefile.am +++ /dev/null @@ -1,2 +0,0 @@ -EXTRA_DIST = \ - org.gtk.GDBus.Examples.ObjectManager.service.in diff --git a/gio/tests/socket-service.c b/gio/tests/socket-service.c index 9ae76d082..5496b69c0 100644 --- a/gio/tests/socket-service.c +++ b/gio/tests/socket-service.c @@ -232,6 +232,316 @@ test_threaded_712570 (void) g_mutex_unlock (&mutex_712570); } +static void +closed_read_write_async_cb (GSocketConnection *conn, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + gboolean res; + + res = g_io_stream_close_finish (G_IO_STREAM (conn), result, &error); + g_assert_no_error (error); + g_assert_true (res); +} + +typedef struct { + GSocketConnection *conn; + guint8 *data; +} WriteAsyncData; + +static void +written_read_write_async_cb (GOutputStream *ostream, + GAsyncResult *result, + gpointer user_data) +{ + WriteAsyncData *data = user_data; + GError *error = NULL; + gboolean res; + gsize bytes_written; + GSocketConnection *conn; + + conn = data->conn; + + g_free (data->data); + g_free (data); + + res = g_output_stream_write_all_finish (ostream, result, &bytes_written, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (bytes_written, ==, 20); + + g_io_stream_close_async (G_IO_STREAM (conn), + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) closed_read_write_async_cb, + NULL); + g_object_unref (conn); +} + +static void +connected_read_write_async_cb (GObject *client, + GAsyncResult *result, + gpointer user_data) +{ + GSocketConnection *conn; + GOutputStream *ostream; + GError *error = NULL; + WriteAsyncData *data; + gsize i; + GSocketConnection **sconn = user_data; + + conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client), result, &error); + g_assert_no_error (error); + g_assert_nonnull (conn); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (conn)); + + data = g_new0 (WriteAsyncData, 1); + data->conn = conn; + data->data = g_new0 (guint8, 20); + for (i = 0; i < 20; i++) + data->data[i] = i; + + g_output_stream_write_all_async (ostream, + data->data, + 20, + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) written_read_write_async_cb, + data /* stolen */); + + *sconn = g_object_ref (conn); +} + +typedef struct { + GSocketConnection *conn; + GOutputVector *vectors; + guint n_vectors; + guint8 *data; +} WritevAsyncData; + +static void +writtenv_read_write_async_cb (GOutputStream *ostream, + GAsyncResult *result, + gpointer user_data) +{ + WritevAsyncData *data = user_data; + GError *error = NULL; + gboolean res; + gsize bytes_written; + GSocketConnection *conn; + + conn = data->conn; + g_free (data->data); + g_free (data); + + res = g_output_stream_writev_all_finish (ostream, result, &bytes_written, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (bytes_written, ==, 20); + + g_io_stream_close_async (G_IO_STREAM (conn), + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) closed_read_write_async_cb, + NULL); + g_object_unref (conn); +} + +static void +connected_read_writev_async_cb (GObject *client, + GAsyncResult *result, + gpointer user_data) +{ + GSocketConnection *conn; + GOutputStream *ostream; + GError *error = NULL; + WritevAsyncData *data; + gsize i; + GSocketConnection **sconn = user_data; + + conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client), result, &error); + g_assert_no_error (error); + g_assert_nonnull (conn); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (conn)); + + data = g_new0 (WritevAsyncData, 1); + data->conn = conn; + data->vectors = g_new0 (GOutputVector, 3); + data->n_vectors = 3; + data->data = g_new0 (guint8, 20); + for (i = 0; i < 20; i++) + data->data[i] = i; + + data->vectors[0].buffer = data->data; + data->vectors[0].size = 5; + data->vectors[1].buffer = data->data + 5; + data->vectors[1].size = 10; + data->vectors[2].buffer = data->data + 15; + data->vectors[2].size = 5; + + g_output_stream_writev_all_async (ostream, + data->vectors, + data->n_vectors, + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) writtenv_read_write_async_cb, + data /* stolen */); + + *sconn = g_object_ref (conn); +} + +typedef struct { + GSocketConnection *conn; + guint8 *data; +} ReadAsyncData; + +static void +read_read_write_async_cb (GInputStream *istream, + GAsyncResult *result, + gpointer user_data) +{ + ReadAsyncData *data = user_data; + GError *error = NULL; + gboolean res; + gsize bytes_read; + GSocketConnection *conn; + const guint8 expected_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + + res = g_input_stream_read_all_finish (istream, result, &bytes_read, &error); + g_assert_no_error (error); + g_assert_true (res); + + g_assert_cmpmem (expected_data, sizeof expected_data, data->data, bytes_read); + + conn = data->conn; + g_object_set_data (G_OBJECT (conn), "test-data-read", GINT_TO_POINTER (TRUE)); + + g_free (data->data); + g_free (data); + + g_io_stream_close_async (G_IO_STREAM (conn), + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) closed_read_write_async_cb, + NULL); + g_object_unref (conn); +} + +static void +incoming_read_write_async_cb (GSocketService *service, + GSocketConnection *conn, + GObject *source_object, + gpointer user_data) +{ + ReadAsyncData *data; + GSocketConnection **cconn = user_data; + GInputStream *istream; + + istream = g_io_stream_get_input_stream (G_IO_STREAM (conn)); + + data = g_new0 (ReadAsyncData, 1); + data->conn = g_object_ref (conn); + data->data = g_new0 (guint8, 20); + + g_input_stream_read_all_async (istream, + data->data, + 20, + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) read_read_write_async_cb, + data /* stolen */); + + *cconn = g_object_ref (conn); +} + +static void +test_read_write_async_internal (gboolean writev) +{ + GInetAddress *iaddr; + GSocketAddress *saddr, *listening_addr; + GSocketService *service; + GError *error = NULL; + GSocketClient *client; + GSocketConnection *sconn = NULL, *cconn = NULL; + + iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); + saddr = g_inet_socket_address_new (iaddr, 0); + g_object_unref (iaddr); + + service = g_socket_service_new (); + + g_socket_listener_add_address (G_SOCKET_LISTENER (service), + saddr, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_TCP, + NULL, + &listening_addr, + &error); + g_assert_no_error (error); + g_object_unref (saddr); + + g_signal_connect (service, "incoming", G_CALLBACK (incoming_read_write_async_cb), &sconn); + + client = g_socket_client_new (); + + if (writev) + g_socket_client_connect_async (client, + G_SOCKET_CONNECTABLE (listening_addr), + NULL, + connected_read_writev_async_cb, + &cconn); + else + g_socket_client_connect_async (client, + G_SOCKET_CONNECTABLE (listening_addr), + NULL, + connected_read_write_async_cb, + &cconn); + + g_object_unref (client); + g_object_unref (listening_addr); + + g_socket_service_start (service); + g_assert_true (g_socket_service_is_active (service)); + + do + { + g_main_context_iteration (NULL, TRUE); + } + while (!sconn || !cconn || + !g_io_stream_is_closed (G_IO_STREAM (sconn)) || + !g_io_stream_is_closed (G_IO_STREAM (cconn))); + + g_assert_true (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (sconn), "test-data-read"))); + + g_object_unref (sconn); + g_object_unref (cconn); + g_object_unref (service); +} + +/* Test if connecting to a socket service and asynchronously writing data on + * one side followed by reading the same data on the other side of the + * connection works correctly + */ +static void +test_read_write_async (void) +{ + test_read_write_async_internal (FALSE); +} + +/* Test if connecting to a socket service and asynchronously writing data on + * one side followed by reading the same data on the other side of the + * connection works correctly. This uses writev() instead of normal write(). + */ +static void +test_read_writev_async (void) +{ + test_read_write_async_internal (TRUE); +} + + int main (int argc, char *argv[]) @@ -242,6 +552,8 @@ main (int argc, g_test_add_func ("/socket-service/start-stop", test_start_stop); g_test_add_func ("/socket-service/threaded/712570", test_threaded_712570); + g_test_add_func ("/socket-service/read_write_async", test_read_write_async); + g_test_add_func ("/socket-service/read_writev_async", test_read_writev_async); return g_test_run(); } diff --git a/gio/tests/socket.c b/gio/tests/socket.c index 9b3adb4e4..8410a3e84 100644 --- a/gio/tests/socket.c +++ b/gio/tests/socket.c @@ -1709,6 +1709,154 @@ test_get_available (gconstpointer user_data) g_object_unref (client); } +typedef struct { + GInputStream *is; + GOutputStream *os; + const guint8 *write_data; + guint8 *read_data; +} TestReadWriteData; + +static gpointer +test_read_write_write_thread (gpointer user_data) +{ + TestReadWriteData *data = user_data; + gsize bytes_written; + GError *error = NULL; + gboolean res; + + res = g_output_stream_write_all (data->os, data->write_data, 1024, &bytes_written, NULL, &error); + g_assert_true (res); + g_assert_no_error (error); + g_assert_cmpint (bytes_written, ==, 1024); + + return NULL; +} + +static gpointer +test_read_write_read_thread (gpointer user_data) +{ + TestReadWriteData *data = user_data; + gsize bytes_read; + GError *error = NULL; + gboolean res; + + res = g_input_stream_read_all (data->is, data->read_data, 1024, &bytes_read, NULL, &error); + g_assert_true (res); + g_assert_no_error (error); + g_assert_cmpint (bytes_read, ==, 1024); + + return NULL; +} + +static gpointer +test_read_write_writev_thread (gpointer user_data) +{ + TestReadWriteData *data = user_data; + gsize bytes_written; + GError *error = NULL; + gboolean res; + GOutputVector vectors[3]; + + vectors[0].buffer = data->write_data; + vectors[0].size = 256; + vectors[1].buffer = data->write_data + 256; + vectors[1].size = 256; + vectors[2].buffer = data->write_data + 512; + vectors[2].size = 512; + + res = g_output_stream_writev_all (data->os, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error); + g_assert_true (res); + g_assert_no_error (error); + g_assert_cmpint (bytes_written, ==, 1024); + + return NULL; +} + +/* test if normal read/write/writev via the GSocket*Streams works on TCP sockets */ +static void +test_read_write (gconstpointer user_data) +{ + gboolean writev = GPOINTER_TO_INT (user_data); + GError *err = NULL; + GSocket *listener, *server, *client; + GInetAddress *addr; + GSocketAddress *saddr; + TestReadWriteData data; + guint8 data_write[1024], data_read[1024]; + GSocketConnection *server_stream, *client_stream; + GThread *write_thread, *read_thread; + guint i; + + listener = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &err); + g_assert_no_error (err); + g_assert (G_IS_SOCKET (listener)); + + client = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &err); + g_assert_no_error (err); + g_assert (G_IS_SOCKET (client)); + + addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); + saddr = g_inet_socket_address_new (addr, 0); + + g_socket_bind (listener, saddr, TRUE, &err); + g_assert_no_error (err); + g_object_unref (saddr); + g_object_unref (addr); + + saddr = g_socket_get_local_address (listener, &err); + g_assert_no_error (err); + + g_socket_listen (listener, &err); + g_assert_no_error (err); + g_socket_connect (client, saddr, NULL, &err); + g_assert_no_error (err); + + server = g_socket_accept (listener, NULL, &err); + g_assert_no_error (err); + g_socket_set_blocking (server, FALSE); + g_object_unref (listener); + + server_stream = g_socket_connection_factory_create_connection (server); + g_assert_nonnull (server_stream); + client_stream = g_socket_connection_factory_create_connection (client); + g_assert_nonnull (client_stream); + + for (i = 0; i < sizeof (data_write); i++) + data_write[i] = i; + + data.is = g_io_stream_get_input_stream (G_IO_STREAM (server_stream)); + data.os = g_io_stream_get_output_stream (G_IO_STREAM (client_stream)); + data.read_data = data_read; + data.write_data = data_write; + + if (writev) + write_thread = g_thread_new ("writer", test_read_write_writev_thread, &data); + else + write_thread = g_thread_new ("writer", test_read_write_write_thread, &data); + read_thread = g_thread_new ("reader", test_read_write_read_thread, &data); + + g_thread_join (write_thread); + g_thread_join (read_thread); + + g_assert_cmpmem (data_write, sizeof data_write, data_read, sizeof data_read); + + g_socket_close (server, &err); + g_assert_no_error (err); + + g_object_unref (server_stream); + g_object_unref (client_stream); + + g_object_unref (saddr); + g_object_unref (server); + g_object_unref (client); +} + int main (int argc, char *argv[]) @@ -1761,6 +1909,10 @@ main (int argc, test_get_available); g_test_add_data_func ("/socket/get_available/stream", GUINT_TO_POINTER (G_SOCKET_TYPE_STREAM), test_get_available); + g_test_add_data_func ("/socket/read_write", GUINT_TO_POINTER (FALSE), + test_read_write); + g_test_add_data_func ("/socket/read_writev", GUINT_TO_POINTER (TRUE), + test_read_write); return g_test_run(); } diff --git a/gio/tests/test1.overlay b/gio/tests/test1.overlay new file mode 100644 index 000000000..687d69b2a --- /dev/null +++ b/gio/tests/test1.overlay @@ -0,0 +1 @@ +It is a beautiful day! diff --git a/gio/tests/unix-streams.c b/gio/tests/unix-streams.c index 67a90d83d..65eedb368 100644 --- a/gio/tests/unix-streams.c +++ b/gio/tests/unix-streams.c @@ -27,6 +27,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <fcntl.h> #define DATA "abcdefghijklmnopqrstuvwxyz" @@ -351,6 +352,476 @@ test_basic (void) g_object_unref (os); } +typedef struct { + GInputStream *is; + GOutputStream *os; + const guint8 *write_data; + guint8 *read_data; +} TestReadWriteData; + +static gpointer +test_read_write_write_thread (gpointer user_data) +{ + TestReadWriteData *data = user_data; + gsize bytes_written; + GError *error = NULL; + gboolean res; + + res = g_output_stream_write_all (data->os, data->write_data, 1024, &bytes_written, NULL, &error); + g_assert_true (res); + g_assert_no_error (error); + g_assert_cmpuint (bytes_written, ==, 1024); + + return NULL; +} + +static gpointer +test_read_write_read_thread (gpointer user_data) +{ + TestReadWriteData *data = user_data; + gsize bytes_read; + GError *error = NULL; + gboolean res; + + res = g_input_stream_read_all (data->is, data->read_data, 1024, &bytes_read, NULL, &error); + g_assert_true (res); + g_assert_no_error (error); + g_assert_cmpuint (bytes_read, ==, 1024); + + return NULL; +} + +static gpointer +test_read_write_writev_thread (gpointer user_data) +{ + TestReadWriteData *data = user_data; + gsize bytes_written; + GError *error = NULL; + gboolean res; + GOutputVector vectors[3]; + + vectors[0].buffer = data->write_data; + vectors[0].size = 256; + vectors[1].buffer = data->write_data + 256; + vectors[1].size = 256; + vectors[2].buffer = data->write_data + 512; + vectors[2].size = 512; + + res = g_output_stream_writev_all (data->os, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error); + g_assert_true (res); + g_assert_no_error (error); + g_assert_cmpuint (bytes_written, ==, 1024); + + return NULL; +} + +/* test if normal writing/reading from a pipe works */ +static void +test_read_write (gconstpointer user_data) +{ + gboolean writev = GPOINTER_TO_INT (user_data); + GUnixInputStream *is; + GUnixOutputStream *os; + gint fd[2]; + guint8 data_write[1024], data_read[1024]; + guint i; + GThread *write_thread, *read_thread; + TestReadWriteData data; + + for (i = 0; i < sizeof (data_write); i++) + data_write[i] = i; + + g_assert_cmpint (pipe (fd), ==, 0); + + is = G_UNIX_INPUT_STREAM (g_unix_input_stream_new (fd[0], TRUE)); + os = G_UNIX_OUTPUT_STREAM (g_unix_output_stream_new (fd[1], TRUE)); + + data.is = G_INPUT_STREAM (is); + data.os = G_OUTPUT_STREAM (os); + data.read_data = data_read; + data.write_data = data_write; + + if (writev) + write_thread = g_thread_new ("writer", test_read_write_writev_thread, &data); + else + write_thread = g_thread_new ("writer", test_read_write_write_thread, &data); + read_thread = g_thread_new ("reader", test_read_write_read_thread, &data); + + g_thread_join (write_thread); + g_thread_join (read_thread); + + g_assert_cmpmem (data_write, sizeof data_write, data_read, sizeof data_read); + + g_object_unref (os); + g_object_unref (is); +} + +/* test if g_pollable_output_stream_write_nonblocking() and + * g_pollable_output_stream_read_nonblocking() correctly return WOULD_BLOCK + * and correctly reset their status afterwards again, and all data that is + * written can also be read again. + */ +static void +test_write_wouldblock (void) +{ +#ifndef F_GETPIPE_SZ + g_test_skip ("F_GETPIPE_SZ not defined"); +#else /* if F_GETPIPE_SZ */ + GUnixInputStream *is; + GUnixOutputStream *os; + gint fd[2]; + GError *err = NULL; + guint8 data_write[1024], data_read[1024]; + guint i; + gint pipe_capacity; + + for (i = 0; i < sizeof (data_write); i++) + data_write[i] = i; + + g_assert_cmpint (pipe (fd), ==, 0); + + g_assert_cmpint (fcntl (fd[0], F_SETPIPE_SZ, 4096, NULL), !=, 0); + pipe_capacity = fcntl (fd[0], F_GETPIPE_SZ, &pipe_capacity, NULL); + g_assert_cmpint (pipe_capacity, >=, 4096); + g_assert_cmpint (pipe_capacity % 1024, >=, 0); + + is = G_UNIX_INPUT_STREAM (g_unix_input_stream_new (fd[0], TRUE)); + os = G_UNIX_OUTPUT_STREAM (g_unix_output_stream_new (fd[1], TRUE)); + + /* Run the whole thing three times to make sure that the streams + * reset the writability/readability state again */ + for (i = 0; i < 3; i++) { + gssize written = 0, written_complete = 0; + gssize read = 0, read_complete = 0; + + do + { + written_complete += written; + written = g_pollable_output_stream_write_nonblocking (G_POLLABLE_OUTPUT_STREAM (os), + data_write, + sizeof (data_write), + NULL, + &err); + } + while (written > 0); + + g_assert_cmpuint (written_complete, >, 0); + g_assert_nonnull (err); + g_assert_error (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK); + g_clear_error (&err); + + do + { + read_complete += read; + read = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (is), + data_read, + sizeof (data_read), + NULL, + &err); + if (read > 0) + g_assert_cmpmem (data_read, read, data_write, sizeof (data_write)); + } + while (read > 0); + + g_assert_cmpuint (read_complete, ==, written_complete); + g_assert_nonnull (err); + g_assert_error (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK); + g_clear_error (&err); + } + + g_object_unref (os); + g_object_unref (is); +#endif /* if F_GETPIPE_SZ */ +} + +/* test if g_pollable_output_stream_writev_nonblocking() and + * g_pollable_output_stream_read_nonblocking() correctly return WOULD_BLOCK + * and correctly reset their status afterwards again, and all data that is + * written can also be read again. + */ +static void +test_writev_wouldblock (void) +{ +#ifndef F_GETPIPE_SZ + g_test_skip ("F_GETPIPE_SZ not defined"); +#else /* if F_GETPIPE_SZ */ + GUnixInputStream *is; + GUnixOutputStream *os; + gint fd[2]; + GError *err = NULL; + guint8 data_write[1024], data_read[1024]; + guint i; + GOutputVector vectors[4]; + GPollableReturn res; + gint pipe_capacity; + + for (i = 0; i < sizeof (data_write); i++) + data_write[i] = i; + + g_assert_cmpint (pipe (fd), ==, 0); + + g_assert_cmpint (fcntl (fd[0], F_SETPIPE_SZ, 4096, NULL), !=, 0); + pipe_capacity = fcntl (fd[0], F_GETPIPE_SZ, &pipe_capacity, NULL); + g_assert_cmpint (pipe_capacity, >=, 4096); + g_assert_cmpint (pipe_capacity % 1024, >=, 0); + + is = G_UNIX_INPUT_STREAM (g_unix_input_stream_new (fd[0], TRUE)); + os = G_UNIX_OUTPUT_STREAM (g_unix_output_stream_new (fd[1], TRUE)); + + /* Run the whole thing three times to make sure that the streams + * reset the writability/readability state again */ + for (i = 0; i < 3; i++) { + gsize written = 0, written_complete = 0; + gssize read = 0, read_complete = 0; + + do + { + written_complete += written; + + vectors[0].buffer = data_write; + vectors[0].size = 256; + vectors[1].buffer = data_write + 256; + vectors[1].size = 256; + vectors[2].buffer = data_write + 512; + vectors[2].size = 256; + vectors[3].buffer = data_write + 768; + vectors[3].size = 256; + + res = g_pollable_output_stream_writev_nonblocking (G_POLLABLE_OUTPUT_STREAM (os), + vectors, + G_N_ELEMENTS (vectors), + &written, + NULL, + &err); + } + while (res == G_POLLABLE_RETURN_OK); + + g_assert_cmpuint (written_complete, >, 0); + g_assert_null (err); + g_assert_cmpint (res, ==, G_POLLABLE_RETURN_WOULD_BLOCK); + /* writev() on UNIX streams either succeeds fully or not at all */ + g_assert_cmpuint (written, ==, 0); + + do + { + read_complete += read; + read = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (is), + data_read, + sizeof (data_read), + NULL, + &err); + if (read > 0) + g_assert_cmpmem (data_read, read, data_write, sizeof (data_write)); + } + while (read > 0); + + g_assert_cmpuint (read_complete, ==, written_complete); + g_assert_nonnull (err); + g_assert_error (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK); + g_clear_error (&err); + } + + g_object_unref (os); + g_object_unref (is); +#endif /* if F_GETPIPE_SZ */ +} + +#ifdef F_GETPIPE_SZ +static void +write_async_wouldblock_cb (GUnixOutputStream *os, + GAsyncResult *result, + gpointer user_data) +{ + gsize *bytes_written = user_data; + GError *err = NULL; + + g_output_stream_write_all_finish (G_OUTPUT_STREAM (os), result, bytes_written, &err); + g_assert_no_error (err); +} + +static void +read_async_wouldblock_cb (GUnixInputStream *is, + GAsyncResult *result, + gpointer user_data) +{ + gsize *bytes_read = user_data; + GError *err = NULL; + + g_input_stream_read_all_finish (G_INPUT_STREAM (is), result, bytes_read, &err); + g_assert_no_error (err); +} +#endif /* if F_GETPIPE_SZ */ + +/* test if the async implementation of write_all() and read_all() in G*Stream + * around the GPollable*Stream API is working correctly. + */ +static void +test_write_async_wouldblock (void) +{ +#ifndef F_GETPIPE_SZ + g_test_skip ("F_GETPIPE_SZ not defined"); +#else /* if F_GETPIPE_SZ */ + GUnixInputStream *is; + GUnixOutputStream *os; + gint fd[2]; + guint8 *data, *data_read; + guint i; + gint pipe_capacity; + gsize bytes_written = 0, bytes_read = 0; + + g_assert_cmpint (pipe (fd), ==, 0); + + /* FIXME: These should not be needed but otherwise + * g_unix_output_stream_write() will block because + * a) the fd is writable + * b) writing 4x capacity will block because writes are atomic + * c) the fd is blocking + * + * See https://gitlab.gnome.org/GNOME/glib/issues/1654 + */ + g_unix_set_fd_nonblocking (fd[0], TRUE, NULL); + g_unix_set_fd_nonblocking (fd[1], TRUE, NULL); + + g_assert_cmpint (fcntl (fd[0], F_SETPIPE_SZ, 4096, NULL), !=, 0); + pipe_capacity = fcntl (fd[0], F_GETPIPE_SZ, &pipe_capacity, NULL); + g_assert_cmpint (pipe_capacity, >=, 4096); + + data = g_new (guint8, 4 * pipe_capacity); + for (i = 0; i < 4 * pipe_capacity; i++) + data[i] = i; + data_read = g_new (guint8, 4 * pipe_capacity); + + is = G_UNIX_INPUT_STREAM (g_unix_input_stream_new (fd[0], TRUE)); + os = G_UNIX_OUTPUT_STREAM (g_unix_output_stream_new (fd[1], TRUE)); + + g_output_stream_write_all_async (G_OUTPUT_STREAM (os), + data, + 4 * pipe_capacity, + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) write_async_wouldblock_cb, + &bytes_written); + + g_input_stream_read_all_async (G_INPUT_STREAM (is), + data_read, + 4 * pipe_capacity, + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) read_async_wouldblock_cb, + &bytes_read); + + while (bytes_written == 0 && bytes_read == 0) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (bytes_written, ==, 4 * pipe_capacity); + g_assert_cmpuint (bytes_read, ==, 4 * pipe_capacity); + g_assert_cmpmem (data_read, bytes_read, data, bytes_written); + + g_free (data); + g_free (data_read); + + g_object_unref (os); + g_object_unref (is); +#endif /* if F_GETPIPE_SZ */ +} + +#ifdef F_GETPIPE_SZ +static void +writev_async_wouldblock_cb (GUnixOutputStream *os, + GAsyncResult *result, + gpointer user_data) +{ + gsize *bytes_written = user_data; + GError *err = NULL; + + g_output_stream_writev_all_finish (G_OUTPUT_STREAM (os), result, bytes_written, &err); + g_assert_no_error (err); +} +#endif /* if F_GETPIPE_SZ */ + +/* test if the async implementation of writev_all() and read_all() in G*Stream + * around the GPollable*Stream API is working correctly. + */ +static void +test_writev_async_wouldblock (void) +{ +#ifndef F_GETPIPE_SZ + g_test_skip ("F_GETPIPE_SZ not defined"); +#else /* if F_GETPIPE_SZ */ + GUnixInputStream *is; + GUnixOutputStream *os; + gint fd[2]; + guint8 *data, *data_read; + guint i; + gint pipe_capacity; + gsize bytes_written = 0, bytes_read = 0; + GOutputVector vectors[4]; + + g_assert_cmpint (pipe (fd), ==, 0); + + /* FIXME: These should not be needed but otherwise + * g_unix_output_stream_writev() will block because + * a) the fd is writable + * b) writing 4x capacity will block because writes are atomic + * c) the fd is blocking + * + * See https://gitlab.gnome.org/GNOME/glib/issues/1654 + */ + g_unix_set_fd_nonblocking (fd[0], TRUE, NULL); + g_unix_set_fd_nonblocking (fd[1], TRUE, NULL); + + g_assert_cmpint (fcntl (fd[0], F_SETPIPE_SZ, 4096, NULL), !=, 0); + pipe_capacity = fcntl (fd[0], F_GETPIPE_SZ, &pipe_capacity, NULL); + g_assert_cmpint (pipe_capacity, >=, 4096); + + data = g_new (guint8, 4 * pipe_capacity); + for (i = 0; i < 4 * pipe_capacity; i++) + data[i] = i; + data_read = g_new (guint8, 4 * pipe_capacity); + + vectors[0].buffer = data; + vectors[0].size = 1024; + vectors[1].buffer = data + 1024; + vectors[1].size = 1024; + vectors[2].buffer = data + 2048; + vectors[2].size = 1024; + vectors[3].buffer = data + 3072; + vectors[3].size = 4 * pipe_capacity - 3072; + + is = G_UNIX_INPUT_STREAM (g_unix_input_stream_new (fd[0], TRUE)); + os = G_UNIX_OUTPUT_STREAM (g_unix_output_stream_new (fd[1], TRUE)); + + g_output_stream_writev_all_async (G_OUTPUT_STREAM (os), + vectors, + G_N_ELEMENTS (vectors), + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) writev_async_wouldblock_cb, + &bytes_written); + + g_input_stream_read_all_async (G_INPUT_STREAM (is), + data_read, + 4 * pipe_capacity, + G_PRIORITY_DEFAULT, + NULL, + (GAsyncReadyCallback) read_async_wouldblock_cb, + &bytes_read); + + while (bytes_written == 0 && bytes_read == 0) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (bytes_written, ==, 4 * pipe_capacity); + g_assert_cmpuint (bytes_read, ==, 4 * pipe_capacity); + g_assert_cmpmem (data_read, bytes_read, data, bytes_written); + + g_free (data); + g_free (data_read); + + g_object_unref (os); + g_object_unref (is); +#endif /* F_GETPIPE_SZ */ +} + int main (int argc, char *argv[]) @@ -365,5 +836,23 @@ main (int argc, GINT_TO_POINTER (TRUE), test_pipe_io); + g_test_add_data_func ("/unix-streams/read_write", + GINT_TO_POINTER (FALSE), + test_read_write); + + g_test_add_data_func ("/unix-streams/read_writev", + GINT_TO_POINTER (TRUE), + test_read_write); + + g_test_add_func ("/unix-streams/write-wouldblock", + test_write_wouldblock); + g_test_add_func ("/unix-streams/writev-wouldblock", + test_writev_wouldblock); + + g_test_add_func ("/unix-streams/write-async-wouldblock", + test_write_async_wouldblock); + g_test_add_func ("/unix-streams/writev-async-wouldblock", + test_writev_async_wouldblock); + return g_test_run(); } diff --git a/gio/win32/Makefile.am b/gio/win32/Makefile.am deleted file mode 100644 index 2fc10f65e..000000000 --- a/gio/win32/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -include $(top_srcdir)/glib.mk - -noinst_LTLIBRARIES += libgiowin32.la - -libgiowin32_la_SOURCES = \ - gwin32fsmonitorutils.c \ - gwin32fsmonitorutils.h \ - gwin32filemonitor.c \ - gwin32filemonitor.h \ - gwinhttpvfs.c \ - gwinhttpvfs.h \ - gwinhttpfile.c \ - gwinhttpfile.h \ - gwinhttpfileinputstream.c \ - gwinhttpfileinputstream.h \ - gwinhttpfileoutputstream.c \ - gwinhttpfileoutputstream.h \ - winhttp.h \ - $(NULL) - -libgiowin32_la_CFLAGS = \ - $(GLIB_HIDDEN_VISIBILITY_CFLAGS) \ - -DG_LOG_DOMAIN=\"GLib-GIO\" \ - $(gio_INCLUDES) \ - $(GLIB_DEBUG_FLAGS) \ - -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \ - -DGIO_COMPILATION \ - -DG_DISABLE_DEPRECATED diff --git a/gio/xdgmime/Makefile.am b/gio/xdgmime/Makefile.am deleted file mode 100644 index 42348a6ab..000000000 --- a/gio/xdgmime/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -include $(top_srcdir)/glib.mk - -AM_CPPFLAGS = -DXDG_PREFIX=_gio_xdg - -noinst_LTLIBRARIES += libxdgmime.la - -libxdgmime_la_CFLAGS = $(GLIB_HIDDEN_VISIBILITY_CFLAGS) -libxdgmime_la_SOURCES = \ - xdgmime.c \ - xdgmime.h \ - xdgmimealias.c \ - xdgmimealias.h \ - xdgmimecache.c \ - xdgmimecache.h \ - xdgmimeglob.c \ - xdgmimeglob.h \ - xdgmimeicon.c \ - xdgmimeicon.h \ - xdgmimeint.c \ - xdgmimeint.h \ - xdgmimemagic.c \ - xdgmimemagic.h \ - xdgmimeparent.c \ - xdgmimeparent.h |