diff -pruN 4.1.1-2/.gitignore 4.2.2-0ubuntu1/.gitignore
--- 4.1.1-2/.gitignore	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/.gitignore	1970-01-01 00:00:00.000000000 +0000
@@ -1,59 +0,0 @@
-# Add patterns in here to exclude files created by tools integrated with this
-# repository, such as test frameworks from the project's recommended workflow,
-# rendered documentation and package builds.
-#
-# Don't add patterns to exclude files created by preferred personal tools
-# (editors, IDEs, your operating system itself even). These should instead be
-# maintained outside the repository, for example in a ~/.gitignore file added
-# with:
-#
-# git config --global core.excludesfile '~/.gitignore'
-
-# Bytecompiled Python
-*.py[cod]
-
-# C extensions
-*.so
-
-# Packages
-*.egg*
-*.egg-info
-dist
-build
-eggs
-parts
-bin
-var
-sdist
-develop-eggs
-.installed.cfg
-lib
-lib64
-
-# Installer logs
-pip-log.txt
-
-# Unit test / coverage reports
-.coverage
-cover
-.tox
-.stestr
-
-# Translations
-*.mo
-
-# Complexity
-output/*.html
-output/*/index.html
-
-# Sphinx
-doc/build
-
-# pbr generates these
-AUTHORS
-ChangeLog
-
-# reno build
-releasenotes/build
-releasenotes/notes/reno.cache
-RELEASENOTES.rst
diff -pruN 4.1.1-2/.gitreview 4.2.2-0ubuntu1/.gitreview
--- 4.1.1-2/.gitreview	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/.gitreview	1970-01-01 00:00:00.000000000 +0000
@@ -1,4 +0,0 @@
-[gerrit]
-host=review.opendev.org
-port=29418
-project=openstack/oslo.service.git
diff -pruN 4.1.1-2/AUTHORS 4.2.2-0ubuntu1/AUTHORS
--- 4.1.1-2/AUTHORS	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/AUTHORS	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1,170 @@
+Accela Zhao <bigzhao@cisco.com>
+Akihiro Motoki <amotoki@gmail.com>
+Alan Pevec <apevec@redhat.com>
+Alberto Murillo <alberto.murillo.silva@intel.com>
+Alessandro Pilotti <apilotti@cloudbasesolutions.com>
+Alex Gaynor <alex.gaynor@gmail.com>
+Alex Holden <alex@alexjonasholden.com>
+Alexander Gorodnev <agorodnev@mirantis.com>
+Alexis Lee <lxsli@hpe.com>
+Allain Legacy <allain.legacy@windriver.com>
+Andreas Jaeger <aj@suse.com>
+Andreas Jaeger <aj@suse.de>
+Angus Salkeld <asalkeld@redhat.com>
+Ann Kamyshnikova <akamyshnikova@mirantis.com>
+Ben Nemec <bnemec@redhat.com>
+Bence Romsics <bence.romsics@gmail.com>
+Bernhard M. Wiedemann <bwiedemann@suse.de>
+Brant Knudson <bknudson@us.ibm.com>
+Brian Elliott <bdelliott@gmail.com>
+Brian Haley <haleyb.dev@gmail.com>
+Carl Baldwin <carl.baldwin@hp.com>
+Cedric Brandily <zzelle@gmail.com>
+Chang Bo Guo <guochbo@cn.ibm.com>
+ChangBo Guo(gcb) <eric.guo@easystack.cn>
+ChangBo Guo(gcb) <glongwave@gmail.com>
+Charles Short <zulcss@gmail.com>
+Christian Berendt <berendt@b1-systems.de>
+Christopher Lefelhocz <christopher.lefelhoc@rackspace.com>
+Chuck Short <chuck.short@canonical.com>
+Claudiu Belu <cbelu@cloudbasesolutions.com>
+Clif Houck <me@clifhouck.com>
+Corey Bryant <corey.bryant@canonical.com>
+Dan Prince <dprince@redhat.com>
+Daniel Bengtsson <dbengt@redhat.com>
+Daniel P. Berrange <berrange@redhat.com>
+Davanum Srinivas (dims) <davanum@gmail.com>
+Davanum Srinivas <davanum@gmail.com>
+Davanum Srinivas <dims@linux.vnet.ibm.com>
+David Ripton <dripton@redhat.com>
+DennyZhang <denny@unitedstack.com>
+Dina Belova <dbelova@mirantis.com>
+Dirk Mueller <dirk@dmllr.de>
+Dmitry Tantsur <dtantsur@redhat.com>
+Doug Hellmann <doug.hellmann@dreamhost.com>
+Doug Hellmann <doug@doughellmann.com>
+Douglas Viroel <viroel@gmail.com>
+Duan Jiong <jduan@fiberhome.com>
+Duc Truong <dtruong@blizzard.com>
+Elena Ezhova <eezhova@mirantis.com>
+Eoghan Glynn <eglynn@redhat.com>
+Eric Brown <browne@vmware.com>
+Eric Fried <efried@us.ibm.com>
+Eric Fried <openstack@fried.cc>
+Eric Guo <eric.guo@easystack.cn>
+Eric Windisch <eric@cloudscaling.com>
+Fengqian.Gao <fengqian.gao@intel.com>
+Flavio Percoco <flaper87@gmail.com>
+Gary Kotton <gkotton@vmware.com>
+Ghanshyam <gmann@ghanshyammann.com>
+Ghanshyam Mann <gmann@ghanshyammann.com>
+Gorka Eguileor <geguileo@redhat.com>
+Hengqing Hu <hudayou@hotmail.com>
+Hervé Beraud <hberaud@redhat.com>
+Ian Wienand <iwienand@redhat.com>
+Ihar Hrachyshka <ihrachys@redhat.com>
+Ilya Shakhat <ishakhat@mirantis.com>
+Iswarya_Vakati <v.iswarya@nectechnologies.in>
+James Carey <jecarey@us.ibm.com>
+James Page <james.page@canonical.com>
+Jason Dunsmore <jasondunsmore@gmail.com>
+Jason Kölker <jason@koelker.net>
+Javeme <zhangmei.li@easystack.cn>
+Javier Pena <jpena@redhat.com>
+Jay Pipes <jaypipes@gmail.com>
+Jeff Peeler <jpeeler@redhat.com>
+Joe Gordon <joe.gordon0@gmail.com>
+Joe Heck <heckj@mac.com>
+John L. Villalovos <john.l.villalovos@intel.com>
+Joshua Harlow <harlowja@gmail.com>
+Joshua Harlow <harlowja@yahoo-inc.com>
+Joshua Harlow <jxharlow@godaddy.com>
+Julia Kreger <juliaashleykreger@gmail.com>
+Julien Danjou <julien@danjou.info>
+Kenneth Giusti <kgiusti@gmail.com>
+Kevin L. Mitchell <kevin.mitchell@rackspace.com>
+Kiall Mac Innes <kiall@managedit.ie>
+Kirill Bespalov <kbespalov@mirantis.com>
+Kurt Taylor <krtaylor@us.ibm.com>
+Marian Horban <mhorban@mirantis.com>
+Mark McClain <mark.mcclain@dreamhost.com>
+Mark McLoughlin <markmc@redhat.com>
+Maru Newby <marun@redhat.com>
+Matt Riedemann <mriedem@us.ibm.com>
+Matthew Treinish <treinish@linux.vnet.ibm.com>
+Michael Johnson <johnsomor@gmail.com>
+Michael Still <mikal@stillhq.com>
+Mitsuru Kanabuchi <kanabuchi.mitsuru@po.ntts.co.jp>
+Mohammed Naser <mnaser@vexxhost.com>
+Moisés Guimarães de Medeiros <moguimar@redhat.com>
+Monty Taylor <mordred@inaugust.com>
+OpenStack Release Bot <infra-root@openstack.org>
+Pavlo Shchelokovskyy <shchelokovskyy@gmail.com>
+Qin Zhao <chaochin@gmail.com>
+Raymond Pekowski <pekowski@gmail.com>
+Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
+Rohit Jaiswal <rohit.jaiswal@hp.com>
+Roman Podoliaka <rpodolyaka@mirantis.com>
+Ronald Bradford <ronald.bradford@gmail.com>
+Russell Bryant <rbryant@redhat.com>
+Sean Dague <sean.dague@samsung.com>
+Sean McGinnis <sean.mcginnis@gmail.com>
+Sean McGinnis <sean_mcginnis@dell.com>
+Sebastian Lohff <sebastian.lohff@sap.com>
+Sergey Kraynev <skraynev@mirantis.com>
+Sergey Lukjanov <slukjanov@mirantis.com>
+Sergey Vilgelm <sergey@vilgelm.info>
+Slawek Kaplonski <skaplons@redhat.com>
+Soren Hansen <soren@linux2go.dk>
+Stephen Finucane <sfinucan@redhat.com>
+Steve Martinelli <stevemar@ca.ibm.com>
+Steven Hardy <shardy@redhat.com>
+Surojit Pathak <suro@yahoo-inc.com>
+Takashi Kajinami <kajinamit@oss.nttdata.com>
+Takashi Natsume <takanattie@gmail.com>
+Terry Wilson <twilson@redhat.com>
+Thomas Herve <therve@redhat.com>
+Thomas Herve <thomas.herve@enovance.com>
+Tianhua Huang <huangtianhua@huawei.com>
+Tom Cammann <tom.cammann@hp.com>
+TommyLike <tommylikehu@gmail.com>
+Tony Breeds <tony@bakeyournoodle.com>
+Victor Sergeyev <vsergeyev@mirantis.com>
+Victor Stinner <vstinner@redhat.com>
+Vu Cong Tuan <tuanvc@vn.fujitsu.com>
+Wenzhi Yu <wenzhi_yu@163.com>
+YuehuiLei <leiyuehui@inspur.com>
+Zane Bitter <zbitter@redhat.com>
+ZhiQiang Fan <aji.zqfan@gmail.com>
+ZhijunWei <wzj334965317@outlook.com>
+ZhongShengping <chdzsp@163.com>
+Zhongyue Luo <zhongyue.nah@intel.com>
+apporc <appleorchard2000@gmail.com>
+caoyuan <cao.yuan@99cloud.net>
+chenke <chen.ke14@zte.com.cn>
+dengzhaosen <dengzhaosen@inspur.com>
+fujioka yuuichi <fujioka-yuuichi@zx.mxh.nes.nec.co.jp>
+gecong1973 <ge.cong@zte.com.cn>
+gongysh <gongysh@cn.ibm.com>
+jacky06 <zhang.min@99cloud.net>
+jun923.gu <gujun1989@126.com>
+lei zhang <shleiz@cn.ibm.com>
+likui <likui@yovole.com>
+lin-hua-cheng <os.lcheng@gmail.com>
+liu-sheng <liusheng@huawei.com>
+liyingjun <yingjun.li@kylin-cloud.com>
+melanie witt <melwittt@gmail.com>
+melissaml <ma.lei@99cloud.net>
+pengyuesheng <pengyuesheng@gohighsec.com>
+ravikumar-venkatesan <ravikumar.venkatesan@hp.com>
+ricolin <rico.lin@easystack.cn>
+sonu.kumar <sonu.kumar@nectechnologies.in>
+stanzgy <stanzgy@gmail.com>
+venkata anil <anilvenkata@redhat.com>
+venkatamahesh <venkatamaheshkotha@gmail.com>
+wu.shiming <wushiming@yovole.com>
+xhzhf <guoyongxhzhf@163.com>
+xuanyandong <xuanyandong@inspur.com>
+yan.haifeng <yanheven@qq.com>
+zhangboye <zhangboye@inspur.com>
+zwei <leidong@unitedstack.com>
diff -pruN 4.1.1-2/ChangeLog 4.2.2-0ubuntu1/ChangeLog
--- 4.1.1-2/ChangeLog	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/ChangeLog	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1,942 @@
+CHANGES
+=======
+
+4.2.2
+-----
+
+* Revert "Fix keyword-arg-before-vararg warning"
+* Don't deprecate the entire oslo\_service.wsgi module
+* Fix inconsistent interface of ThreadGroup
+
+4.2.1
+-----
+
+* Fix crash during shutdown by using public shutdown() method
+* Accept wait\_interval and support multiple launch\_service() calls
+* Fix keyword-arg-before-vararg warning
+* Fix ServiceBase mapping when using threading backend
+* add pyproject.toml to support pip 23.1
+
+4.2.0
+-----
+
+* Add threading backend implementation using cotyledon and standard threads
+* Replace os.popen
+* Drop redundant injection of VIRTUAL\_ENV
+* Update master for stable/2025.1
+
+4.1.1
+-----
+
+* Fix removed public API
+
+4.1.0
+-----
+
+* Migrate Eventlet components to the new backend system
+* Introduce configurable backend system in oslo.service
+
+4.0.0
+-----
+
+* reno: Update master for unmaintained/2023.1
+
+3.6.0
+-----
+
+* Add note about requirements lower bounds
+* Run pyupgrade to clean up Python 2 syntaxes
+* Remove Python 3.8 support
+* Fix outdated tox minversion
+* Declare Python 3.12 support
+* Remove workaround for eventlet < 0.27.0
+* Update master for stable/2024.2
+
+3.5.0
+-----
+
+* reno: Update master for unmaintained/zed
+* Remove old excludes
+* Update sslutils.wrap for newer Pythons
+* Remove deprecated ssl options
+* Make signal handling order predictable
+* Update master for stable/2024.1
+* reno: Update master for unmaintained/xena
+* reno: Update master for unmaintained/wallaby
+* reno: Update master for unmaintained/victoria
+
+3.4.0
+-----
+
+* Switch to coverage command
+* reno: Update master for unmaintained/yoga
+* pre-commit: Integrate doc8 and bandit
+* pre-commit: Bump versions
+* Bump hacking
+* Update python classifier in setup.cfg
+
+3.3.0
+-----
+
+* Update master for stable/2023.2
+
+3.2.0
+-----
+
+* Imported Translations from Zanata
+* Bump bandit
+* Revert "Moves supported python runtimes from version 3.8 to 3.10"
+* Moves supported python runtimes from version 3.8 to 3.10
+* Update master for stable/2023.1
+
+3.1.1
+-----
+
+* Fix issues related to tox4
+
+3.1.0
+-----
+
+* Fix misuse of assertTrue
+* Imported Translations from Zanata
+* Add Python3 antelope unit tests
+* Update master for stable/zed
+* Fix native threads on child process
+
+3.0.0
+-----
+
+* Imported Translations from Zanata
+* Drop python3.6/3.7 support in testing runtime
+* Add Python3 zed unit tests
+* Update master for stable/yoga
+
+2.8.0
+-----
+
+* Make debug option of wsgi server configurable
+
+2.7.0
+-----
+
+* Fix fo() backdoor command for non-class objects
+* Fix BackOffLoopingCall error so it is not misleading
+* Add Python3 yoga unit tests
+* Update master for stable/xena
+
+2.6.0
+-----
+
+* setup.cfg: Replace dashes with underscores
+* Remove references to 'sys.version\_info'
+* Move flake8 as a pre-commit local target
+* Add Python3 xena unit tests
+* Update master for stable/wallaby
+* Remove lower-constraints remnants
+
+2.5.0
+-----
+
+* remove unicode from code
+* Use TOX\_CONSTRAINTS\_FILE
+* Dropping lower constraints testing
+* Drop custom implementation of EVENTLET\_HUB
+* Use TOX\_CONSTRAINTS\_FILE
+* Use py3 as the default runtime for tox
+* Use TOX\_CONSTRAINTS\_FILE
+* Add Python3 wallaby unit tests
+* Update master for stable/victoria
+* Adding pre-commit
+
+2.4.0
+-----
+
+* [goal] Migrate testing to ubuntu focal
+* Bump bandit version
+
+2.3.2
+-----
+
+* Do not import "oslo.log" in the main module
+
+2.3.1
+-----
+
+
+2.3.0
+-----
+
+* Fix wsgi SSL tests for wsgi module under python 3
+* Reactivate wsgi test related to socket option under python 3
+* Fix wsgi/SSL/ipv6 tests for wsgi module under python 3
+* Fix some SSL tests for wsgi module under python 3
+* Raise minimum version of eventlet to 0.25.2
+* Fix pygments style
+* Stop to use the \_\_future\_\_ module
+
+2.2.0
+-----
+
+* Drop six usages
+* Fix hacking min version to 3.0.1
+* Switch to newer openstackdocstheme and reno versions
+* Remove the unused coding style modules
+* Remove translation sections from setup.cfg
+* Align tests with monkey patch original current\_thread \_active
+* Remove monotonic usage
+* Align contributing doc with oslo's policy
+* Monkey patch original current\_thread \_active
+* Bump default tox env from py37 to py38
+* Add py38 package metadata
+* Use unittest.mock instead of third party mock
+* Add release notes links to doc index
+* Add Python3 victoria unit tests
+* Update master for stable/ussuri
+* Cleanup py27 support
+
+2.1.1
+-----
+
+* Update hacking for Python3
+
+2.1.0
+-----
+
+* Update eventlet
+* Update the minversion parameter
+* remove outdated header
+* reword releasenote for py27 support dropping
+
+2.0.0
+-----
+
+* [ussuri][goal] Drop python 2.7 support and testing
+* tox: Trivial cleanup
+
+1.41.1
+------
+
+* Add 'is\_available' function
+* tox: Keeping going with docs
+* Switch to official Ussuri jobs
+* Extend test cert validity to 2049
+* Update the constraints url
+
+1.41.0
+------
+
+* Update master for stable/train
+
+1.40.2
+------
+
+* Reno for SIGHUP fix
+
+1.40.1
+------
+
+* Polish usage.rst
+* restart: don't stop process on sighup when mutating
+* Move doc related modules to doc/requirements.txt
+* Add Python 3 Train unit tests
+
+1.40.0
+------
+
+* Stop using pbr to build docs
+* Make PID availabe as formatstring in backdoor path
+
+1.39.0
+------
+
+* Cap Bandit below 1.6.0 and update Sphinx requirement
+* Add workers' type check before launching the services
+* Replace git.openstack.org URLs with opendev.org URLs
+* OpenDev Migration Patch
+* Dropping the py35 testing
+* Update master for stable/stein
+
+1.38.0
+------
+
+* Update oslo.service to require yappi 1.0 or newer
+* add python 3.7 unit test job
+* Update hacking version
+
+1.37.0
+------
+
+* Bump oslo.utils lower constraint to 3.40.2
+
+1.36.0
+------
+
+* Profile Oslo Service processes
+* Use eventletutils Event class
+* Avoid eventlet\_backdoor listing on same port
+
+1.35.0
+------
+
+* Use template for lower-constraints
+* Deprecate the ThreadGroup.cancel() API
+* Document the threadgroup module
+* Actually test child SIGHUP signal
+* Restore correct signal handling in Python3
+* Add stop\_on\_exception to TG timers
+* Add better timer APIs to ThreadGroup
+* Update mailinglist from dev to discuss
+* Use SleepFixture in looping call test suite
+
+1.33.0
+------
+
+* Fixture to mock loopingcall wait()
+* Limit monotonic to py2
+
+1.32.1
+------
+
+* Fix stop of loopingcall
+* Use eventlet Event for loopingcall events
+* Clean up .gitignore references to personal tools
+* Always build universal wheels
+
+1.32.0
+------
+
+* Ensure connection is active in graceful shutdown tests
+* Stop asserting on Eventlet internals
+* Skips signal handling on Windows
+* add lib-forward-testing-python3 test job
+* add python 3.6 unit test job
+* import zuul job settings from project-config
+* Update reno for stable/rocky
+
+1.31.3
+------
+
+* Remove unnecessary pyNN testenv
+* Convert oslo.service to using stestr
+* Add release notes link to README
+* Fix oslo.service ProcessLauncher fails to call stop
+* fix tox python3 overrides
+* Add test dependency on requests
+* Remove moxstubout
+
+1.31.2
+------
+
+* [ThreadGroup] Don't remove timer when stop timers
+* Make lower-constraints job voting
+* tox.ini: Use python3.5 in py35 environment
+* Python 3: Fix eventlet wakeup after signal
+* Python 3: Fix non-deterministic test
+* Remove stale pip-missing-reqs tox test
+* Trivial: Update pypi url to new url
+* add lower-constraints job
+* move doc8 test to pep8 job
+* set default python to python3
+
+1.31.1
+------
+
+* Revert "Revert "Permit aborting loopingcall while sleeping""
+
+1.31.0
+------
+
+* Remove eventlet cap
+* Fixup certificates and skip failing test
+
+1.30.0
+------
+
+* Imported Translations from Zanata
+* Imported Translations from Zanata
+* Update links in README
+* Imported Translations from Zanata
+* Updated from global requirements
+* Update reno for stable/queens
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+
+1.29.0
+------
+
+* Maintain shared memory after fork in Python >=3.7
+* Updated from global requirements
+* Revert "Permit aborting loopingcall while sleeping"
+
+1.28.1
+------
+
+
+1.28.0
+------
+
+* Remove -U from pip install
+* Avoid tox\_install.sh for constraints support
+* Updated from global requirements
+* Remove setting of version/release from releasenotes
+* Updated from global requirements
+
+1.27.0
+------
+
+* Updated from global requirements
+* change periodic\_task to catch all exceptions including BaseException
+* Fix bandit scan and make it voting
+* Imported Translations from Zanata
+
+1.26.0
+------
+
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Imported Translations from Zanata
+* Updated from global requirements
+* Updated from global requirements
+* Update reno for stable/pike
+* Updated from global requirements
+
+1.25.0
+------
+
+* Update URLs in documents according to document migration
+
+1.24.1
+------
+
+* rearrange existing documentation to fit the new standard layout
+* switch from oslosphinx to openstackdocstheme
+
+1.24.0
+------
+
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Permit aborting loopingcall while sleeping
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+
+1.23.0
+------
+
+* Add min\_interval to BackOffLoopingCall
+
+1.22.0
+------
+
+* Updated from global requirements
+* Updated from global requirements
+
+1.21.0
+------
+
+* Remove log translations
+* Use Sphinx 1.5 warning-is-error
+* Fix some reST field lists in docstrings
+* Updated from global requirements
+
+1.20.0
+------
+
+* Updated from global requirements
+* [Fix gate]Update test requirement
+* Updated from global requirements
+* Updated from global requirements
+* Fix race condition with fast threads
+* pbr.version.VersionInfo needs package name (oslo.xyz and not oslo\_xyz)
+* Remove duplicated register\_opts call
+* Update reno for stable/ocata
+* Remove references to Python 3.4
+
+1.19.0
+------
+
+* Add FixedIntervalWithTimeoutLoopingCall
+* Add Constraints support
+* Show team and repo badges on README
+
+1.18.0
+------
+
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Imported Translations from Zanata
+* Update .coveragerc after the removal of respective directory
+* Delete python bytecode file
+
+1.17.0
+------
+
+* Changed the home-page link
+* Updated from global requirements
+* Replace 'MagicMock' with 'Mock'
+* Enable release notes translation
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+
+1.16.0
+------
+
+* Updated from global requirements
+* Stay alive on double SIGHUP
+
+1.15.0
+------
+
+* Updated from global requirements
+
+1.14.0
+------
+
+* Updated from global requirements
+* Fix parameters of assertEqual are misplaced
+
+1.13.0
+------
+
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Add reno for release notes management
+* Updated from global requirements
+
+1.12.0
+------
+
+* Imported Translations from Zanata
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+
+1.11.0
+------
+
+* Trivial: ignore openstack/common in flake8 exclude list
+
+1.10.0
+------
+
+* [Trivial] Remove executable privilege of doc/source/conf.py
+
+1.9.0
+-----
+
+* Updated from global requirements
+* Offer mutate\_config\_files
+* Make \_spawn\_service more flexible
+* Remove direct dependency on babel
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Fix argument type for \_sd\_notify() on python3
+* Use a timeutils.StopWatch for cancel timing
+* Add ability to cancel Threads and ThreadGroups
+* Exception: message need '\_' function
+* Fix Heartbeats stop when time is changed
+* Updated from global requirements
+
+1.7.0
+-----
+
+* Updated from global requirements
+* Correct some help text
+* Fix typo in help text
+* wsgi: decrease the default number of greenthreads in pool
+* Updated from global requirements
+
+1.6.0
+-----
+
+* Updated from global requirements
+* Allow the backdoor to serve from a local unix domain socket
+* Updated from global requirements
+
+1.5.0
+-----
+
+* Use requests in TestWSGIServerWithSSL instead of raw socket client
+
+1.4.0
+-----
+
+* Updated from global requirements
+* Updated from global requirements
+* Fix misspelling and rewrite sentence
+* Add a more useful/detailed frame dumping function
+* Updated from global requirements
+* Update translation setup
+* Fix race condition on handling signals
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Fix artificial service.wait()
+
+1.3.0
+-----
+
+* Graceful shutdown added to ServiceLauncher
+* Fix test execution on CentOS 7
+* Updated from global requirements
+* Fix some inconsistency in docstrings
+* Refactoring of tests/eventlet\_service.py
+* Updated from global requirements
+* Remove argument ServiceLauncher.wait() method
+* fix a couple of assert issues
+* Run sslutils and wsgi tests for python3
+* Updated from global requirements
+
+1.2.0
+-----
+
+* Updated from global requirements
+* Fix a race condition in signal handlers
+* Enable py3 mock.patch of RuntimeError
+* Delete python bytecode before every test run
+* Trival: Remove 'MANIFEST.in'
+
+1.1.0
+-----
+
+* Avoid warning when time taken is close to zero
+* Update the \_i18n.py file and fix the domain value
+* Add Bandit to tox for security static analysis
+* Code refactoring of ThreadGroup::stop\_timers()
+
+1.0.0
+-----
+
+* Updated from global requirements
+* Updated from global requirements
+* Add functionality for creating Unix domain WSGI servers
+* Use reflection.get\_class\_name() from oslo.utils
+* Remove Python 2.6 classifier
+* Remove openstack-common.conf
+* cleanup tox.ini
+* Change "started child" messages to DEBUG
+* Support for SSL protocol and cipher controls
+
+0.13.0
+------
+
+* Default value of graceful\_shutdown\_timeout is set to 60sec
+* Updated from global requirements
+* Logger name argument was added into wsgi.Server constructor
+* Avoid the dual-naming confusion
+* Forbid launching services with 0 or negative number of workers
+
+0.12.0
+------
+
+* Document graceful\_shutdown\_timeout config option
+* Remove py26 env from test list
+* Added config option graceful\_shutdown\_timeout
+* Updated from global requirements
+* Add docstring for LoopingCallBase.\_start()
+* Updated from global requirements
+
+0.11.0
+------
+
+* Updated from global requirements
+* Add doc8 to py27 tox env and fix raised issues
+* Document termination of children on SIGHUP
+* Updated from global requirements
+* Updated from global requirements
+
+0.10.0
+------
+
+* RetryDecorator should not log warnings/errors for expected exceptions
+* Termination children on SIGHUP added
+* Fix coverage configuration and execution
+* Add register\_opts function to sslutils
+* Move the common thread manipulating routine to a shared routine
+* Update log string to correctly denote what it waits on
+* Avoid removing entries for timers that didn't stop
+* Cleanup thread on thread done callback
+* Move 'history' -> release notes section
+* Add unit tests for sslutils
+* Expand README and clean up intro to sphinx docs
+* Add shields.io version/downloads links/badges into README.rst
+* add auto-generated docs for config options
+* Move backoff looping call from IPA to oslo.service
+* Change ignore-errors to ignore\_errors
+* Fix the home-page value in setup.cfg
+* WSGI module was corrected
+* Updated from global requirements
+* ThreadGroup's stop didn't recognise the current thread correctly
+* doing monkey\_patch for unittest
+
+0.9.0
+-----
+
+* Handling corner cases in dynamic looping call
+* Change DEBUG log in loopingcall to TRACE level log
+* Updated from global requirements
+
+0.8.0
+-----
+
+* Added wsgi functionality
+
+0.7.0
+-----
+
+* Updated from global requirements
+* Update "Signal handling" section of usage docs
+* Use oslo\_utils reflection to get 'f' callable name
+* Updated from global requirements
+* Prefix the 'safe\_wrapper' function to be '\_safe\_wrapper'
+* Setup translations
+* Check that sighup is supported before accessing signal.SIGHUP
+* Use contextlib.closing instead of try ... finally: sock.close
+* Avoid using the global lockutils semaphore collection
+* Updated from global requirements
+
+0.6.0
+-----
+
+* Added newline at end of file
+* Added class SignalHandler
+* Updated from global requirements
+* Activate pep8 check that \_ is imported
+* Denote what happens when no exceptions are passed in
+* Allow LoopingCall to continue on exception in callee
+
+0.5.0
+-----
+
+* Updated from global requirements
+* Updated from global requirements
+* Updated from global requirements
+* Add oslo\_debug\_helper to tox.ini
+* Add usage documentation for oslo\_service.service module
+
+0.4.0
+-----
+
+* Updated from global requirements
+* save docstring, name etc using six.wraps
+* Move backdoor-related tests from test\_service
+* Add mock to test\_requirements
+* Remove usage of mox in test\_eventlet\_backdoor
+
+0.3.0
+-----
+
+* Copy RetryDecorator from oslo.vmware
+* Increase test coverage of systemd
+* Ensure we set the event and wait on the timer in the test
+* Make it easier to use the eventlet backdoor locally
+* Track created thread and disallow more than one start being active
+
+0.2.0
+-----
+
+* Documentation on the use of the oslo-config-generator
+* Add greenlet to requirements
+* Add tox target to find missing requirements
+* Enforce H405 check
+* Enforce H301 check
+* Return timer after adding it to internal list
+* Updated from global requirements
+* Have all the looping calls share a common run loop
+* Move service abstract base class check to launch\_service methods
+* Fix a typo in a comment
+* Updated from global requirements
+* Use a signal name->sigval and sigval->signal name mapping
+
+0.1.0
+-----
+
+* Test for instantaneous shutdown fixed
+* Graceful shutdown WSGI/RPC server
+* Use monotonic.monotonic and stopwatches instead of time.time
+* Updated from global requirements
+* Eventlet service fixed
+* Add documentation for the service module
+* Improve test coverage for loopingcall module
+* Add oslo.service documentation
+* Remove usage of global CONF
+* Make logging option values configurable
+* Introduce abstract base class for services
+* Add entrypoints for option discovery
+* Updated from global requirements
+* Move the option definitions into a private file
+* Fix unit tests
+* Fix pep8
+* exported from oslo-incubator by graduate.sh
+* Clean up logging to conform to guidelines
+* Port service to Python 3
+* Test for shutting down eventlet server on signal
+* service child process normal SIGTERM exit
+* Revert "Revert "Revert "Optimization of waiting subprocesses in ProcessLauncher"""
+* Revert "Revert "Optimization of waiting subprocesses in ProcessLauncher""
+* Revert "Optimization of waiting subprocesses in ProcessLauncher"
+* ProcessLauncher: reload config file in parent process on SIGHUP
+* Add check to test\_\_signal\_handlers\_set
+* Store ProcessLauncher signal handlers on class level
+* Remove unused validate\_ssl\_version
+* Update tests for optional sslv3
+* Fixed ssl.PROTOCOL\_SSLv3 not supported by Python 2.7.9
+* Optimization of waiting subprocesses in ProcessLauncher
+* Switch from oslo.config to oslo\_config
+* Change oslo.config to oslo\_config
+* Remove oslo.log code and clean up versionutils API
+* Replace mox by mox3
+* Allow overriding name for periodic tasks
+* Separate add\_periodic\_task from the metaclass \_\_init\_\_
+* Upgrade to hacking 0.10
+* Remove unnecessary import of eventlet
+* Added graceful argument on Service.stop method
+* Remove extra white space in log message
+* Prefer delayed %r formatting over explicit repr use
+* ServiceRestartTest: make it more resilient
+* threadgroup: don't log GreenletExit
+* add list\_opts to all modules with configuration options
+* Remove code that moved to oslo.i18n
+* Remove graduated test and fixtures libraries
+* rpc, notifier: remove deprecated modules
+* Let oslotest manage the six.move setting for mox
+* Remove usage of readlines()
+* Allow test\_service to run in isolation
+* Changes calcuation of variable delay
+* Use timestamp in loopingcall
+* Remove unnecessary setUp function
+* Log the function name of looping call
+* pep8: fixed multiple violations
+* Make periodic tasks run on regular spacing interval
+* Use moxstubout and mockpatch from oslotest
+* Implement stop method in ProcessLauncher
+* Fix parenthesis typo misunderstanding in periodic\_task
+* Fix docstring indentation in systemd
+* Remove redundant default=None for config options
+* Make unspecified periodic spaced tasks run on default interval
+* Make stop\_timers() method public
+* Remove deprecated LoopingCall
+* Fixed several typos
+* Add graceful stop function to ThreadGroup.stop
+* Use oslotest instead of common test module
+* Remove duplicated "caught" message
+* Move notification point to a better place
+* Remove rendundant parentheses of cfg help strings
+* Adds test condition in test\_periodic
+* Fixed spelling error - occured to occurred
+* Add missing \_LI for LOG.info in service module
+* notify calling process we are ready to serve
+* Reap child processes gracefully if greenlet thread gets killed
+* Improve help strings for sslutils module
+* Remove unnecessary usage of noqa
+* Removes use of timeutils.set\_time\_override
+* Update oslo log messages with translation domains
+* Refactor unnecessary arithmetic ops in periodic\_task
+* Refactor if logic in periodic\_task
+* Use timestamp in periodic tasks
+* Add basic Python 3 tests
+* Clear time override in test\_periodic
+* Don't share periodic\_task instance data in a class attr
+* Revert "service: replace eventlet event by threading"
+* Simplify launch method
+* Simple typo correction
+* Cleanup unused log related code
+* Utilizes assertIsNone and assertIsNotNone
+* Fix filter() usage due to python 3 compability
+* Use hacking import\_exceptions for gettextutils.\_
+* threadgroup: use threading rather than greenthread
+* disable SIGHUP restart behavior in foreground
+* service: replace eventlet event by threading
+* Allow configurable ProcessLauncher liveness check
+* Make wait & stop methods work on all threads
+* Typos fix in db and periodic\_task module
+* Remove vim header
+* os.\_exit in \_start\_child may cause unexpected exception
+* Adjust import order according to PEP8 imports rule
+* Add a link method to Thread
+* Use multiprocessing.Event to ensure services have started
+* Apply six for metaclass
+* Removed calls to locals()
+* Move comment in service.py to correct location
+* Fixes issue with SUGHUP in services on Windows
+* Replace using tests.utils part2
+* Bump hacking to 0.7.0
+* Replace using tests.utils with openstack.common.test
+* Refactors boolean returns
+* Add service restart function in oslo-incubator
+* Fix stylistic problems with help text
+* Enable H302 hacking check
+* Convert kombu SSL version string into integer
+* Allow launchers to be stopped multiple times
+* Ignore any exceptions from rpc.cleanup()
+* Add graceful service shutdown support to Launcher
+* Improve usability when backdoor\_port is nonzero
+* Enable hacking H404 test
+* Enable hacking H402 test
+* Enable hacking H401 test
+* Fixes import order nits
+* Add DynamicLoopCall timers to ThreadGroups
+* Pass backdoor\_port to services being launched
+* Improve python3 compatibility
+* Use print\_function \_\_future\_\_ import
+* Improve Python 3.x compatibility
+* Import nova's looping call
+* Copy recent changes in periodic tasks from nova
+* Fix IBM copyright strings
+* Removes unused imports in the tests module
+* update OpenStack, LLC to OpenStack Foundation
+* Add function for listing native threads to eventlet backdoor
+* Use oslo-config-2013.1b3
+* Support for SSL in wsgi.Service
+* Replace direct use of testtools BaseTestCase
+* Use testtools as test base class
+* ThreadGroup remove unused name parameters
+* Implement importutils.try\_import
+* Fix test cases in tests.unit.test\_service
+* Don't rely on os.wait() blocking
+* Use Service thread group for WSGI request handling
+* Make project pyflakes clean
+* Replace try: import with extras.try\_import
+* raise\_on\_error parameter shouldn't be passed to task function
+* Account for tasks duration in LoopingCall delay
+* updating sphinx documentation
+* Enable eventlet\_backdoor to return port
+* Use the ThreadGroup for the Launcher
+* Change RPC cleanup ordering
+* threadgroup : greethread.cancel() should be kill()
+* Use spawn\_n when not capturing return value
+* Make ThreadGroup derived from object to make mocking possible
+* Don't log exceptions for GreenletExit and thread\_done
+* Log CONF from ProcessLauncher.wait, like ServiceLauncher
+* Import order clean-up
+* Added a missing \`cfg\` import in service.py
+* Log config on startup
+* Integrate eventlet backdoor
+* Add the rpc service and delete manager
+* Use pep8 v1.3.3
+* Add threadgroup to manage timers and greenthreads
+* Add basic periodic task infrastructure
+* Add multiprocess service launcher
+* Add signal handling to service launcher
+* Basic service launching infrastructure
+* Move manager.py and service.py into common
+* Copy eventlet\_backdoor into common from nova
+* Copy LoopingCall from nova for service.py
+* initial commit
+* Initial skeleton project
diff -pruN 4.1.1-2/PKG-INFO 4.2.2-0ubuntu1/PKG-INFO
--- 4.1.1-2/PKG-INFO	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/PKG-INFO	2025-07-10 09:43:35.980527200 +0000
@@ -0,0 +1,72 @@
+Metadata-Version: 2.1
+Name: oslo.service
+Version: 4.2.2
+Summary: oslo.service library
+Home-page: https://docs.openstack.org/oslo.service/latest/
+Author: OpenStack
+Author-email: openstack-discuss@lists.openstack.org
+Classifier: Environment :: OpenStack
+Classifier: Intended Audience :: Information Technology
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Requires-Python: >=3.9
+License-File: LICENSE
+Requires-Dist: WebOb>=1.7.1
+Requires-Dist: debtcollector>=1.2.0
+Requires-Dist: eventlet>=0.27.0
+Requires-Dist: greenlet>=0.4.15
+Requires-Dist: oslo.utils>=3.40.2
+Requires-Dist: oslo.concurrency>=3.25.0
+Requires-Dist: oslo.config>=5.1.0
+Requires-Dist: oslo.log>=3.36.0
+Requires-Dist: oslo.i18n>=3.15.3
+Requires-Dist: PasteDeploy>=1.5.0
+Requires-Dist: Routes>=2.3.1
+Requires-Dist: Paste>=2.0.2
+Requires-Dist: Yappi>=1.0
+Provides-Extra: threading
+Requires-Dist: cotyledon>=2.0.0; extra == "threading"
+Requires-Dist: futurist>=3.1.1; extra == "threading"
+
+========================
+Team and repository tags
+========================
+
+.. image:: https://governance.openstack.org/tc/badges/oslo.service.svg
+    :target: https://governance.openstack.org/tc/ference/tags/index.html
+
+.. Change things from this point on
+
+========================================================
+ oslo.service -- Library for running OpenStack services
+========================================================
+
+.. image:: https://img.shields.io/pypi/v/oslo.service.svg
+    :target: https://pypi.org/project/oslo.service/
+    :alt: Latest Version
+
+.. image:: https://img.shields.io/pypi/dm/oslo.service.svg
+    :target: https://pypi.org/project/oslo.service/
+    :alt: Downloads
+
+oslo.service provides a framework for defining new long-running
+services using the patterns established by other OpenStack
+applications. It also includes utilities long-running applications
+might need for working with SSL or WSGI, performing periodic
+operations, interacting with systemd, etc.
+
+* Free software: Apache license
+* Documentation: https://docs.openstack.org/oslo.service/latest/
+* Source: https://opendev.org/openstack/oslo.service
+* Bugs: https://bugs.launchpad.net/oslo.service
+* Release notes: https://docs.openstack.org/releasenotes/oslo.service/
+
diff -pruN 4.1.1-2/debian/changelog 4.2.2-0ubuntu1/debian/changelog
--- 4.1.1-2/debian/changelog	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/changelog	2025-08-20 09:16:35.000000000 +0000
@@ -1,227 +1,268 @@
-python-oslo.service (4.1.1-2) unstable; urgency=medium
+python-oslo.service (4.2.2-0ubuntu1) questing; urgency=medium
 
-  * Uploading to unstable.
+  * New upstream release for OpenStack Flamingo. (LP: #2116155)
+  * d/control: ensure cotyledon and futurist are a runtime dependency
+    needed by the threading backend of oslo.service.
 
- -- Thomas Goirand <zigo@debian.org>  Fri, 28 Mar 2025 10:36:58 +0100
+ -- Guillaume Boutry <guillaume.boutry@canonical.com>  Wed, 20 Aug 2025 11:16:35 +0200
 
-python-oslo.service (4.1.1-1) experimental; urgency=medium
+python-oslo.service (4.2.1+git2025070911.501b9e8-0ubuntu2) questing; urgency=medium
 
-  * New upstream release.
+  * d/control: Remove python3-yappi from Depends. 
 
- -- Thomas Goirand <zigo@debian.org>  Thu, 27 Feb 2025 11:23:43 +0100
+ -- Myles Penner <myles.penner@canonical.com>  Mon, 28 Jul 2025 07:48:59 -0700
 
-python-oslo.service (4.1.0-2) experimental; urgency=medium
+python-oslo.service (4.2.1+git2025070911.501b9e8-0ubuntu1) questing; urgency=medium
 
-  * d/watch: switch to version=4 and mode=git.
+  [ Guillaume Boutry ]
+  * d/gbp.conf, .launchpad.yaml: Sync from cloud-archive-tools for
+    flamingo.
 
- -- Thomas Goirand <zigo@debian.org>  Tue, 25 Feb 2025 16:40:04 +0100
+  [ Myles Penner ]
+  * New upstream release for OpenStack Flamingo. (LP: #2116155)
+  * d/s/options: Ignore .launchpad.yaml when generating diffs.
+  * d/p/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch: Refresh.
+  * d/p/0002-Revert-Profile-Oslo-Service-processes.patch: Refresh. 
+  * d/control: Align (Build-)Depends with upstream.
 
-python-oslo.service (4.1.0-1) experimental; urgency=medium
+ -- Myles Penner <myles.penner@canonical.com>  Thu, 17 Jul 2025 11:20:57 -0700
 
-  * New upstream release.
+python-oslo.service (4.1.1-0ubuntu1) plucky; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 24 Feb 2025 17:43:16 +0100
+  * New upstream release for OpenStack Epoxy.
 
-python-oslo.service (3.5.0-3) unstable; urgency=medium
+ -- James Page <james.page@ubuntu.com>  Thu, 27 Feb 2025 12:54:01 +0000
 
-  * Switch to pybuild (Closes: #1090579).
+python-oslo.service (4.0.0-0ubuntu1) plucky; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Thu, 19 Dec 2024 08:31:00 +0100
+  * New upstream release for OpenStack Epoxy.
 
-python-oslo.service (3.5.0-2) unstable; urgency=medium
+ -- James Page <james.page@ubuntu.com>  Thu, 13 Feb 2025 15:45:07 +0000
 
-  * Uploading to unstable.
+python-oslo.service (3.6.0-0ubuntu1) plucky; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Thu, 19 Sep 2024 18:00:36 +0200
+  * d/gbp.conf: upstream-branch -> upstream-dalmatian.
+  * d/gbp.conf, .launchpad.yaml: Sync from cloud-archive-tools for
+    epoxy.
+  * New upstream release for OpenStack Epoxy.
+  * d/control: Drop dependencies that are no longer required.
+  * d/control: Align (Build-)Depends with upstream.
 
-python-oslo.service (3.5.0-1) experimental; urgency=medium
+ -- James Page <james.page@ubuntu.com>  Fri, 17 Jan 2025 09:04:44 +0000
 
-  * New upstream release.
+python-oslo.service (3.5.0-0ubuntu1) oracular; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 26 Aug 2024 17:23:50 +0200
+  * New upstream release.
+  * d/p/py312-compat.patch: Drop, included in release.
+  * d/{control,tests}/*: Test with all supported Pythons.
 
-python-oslo.service (3.4.1-1) unstable; urgency=medium
+ -- James Page <james.page@ubuntu.com>  Mon, 05 Aug 2024 11:36:50 +0100
 
-  * New upstream point release.
+python-oslo.service (3.4.0-0ubuntu1) noble; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Wed, 19 Jun 2024 11:20:15 +0200
+  * New upstream release for OpenStack Caracal. 
+  * d/p/py312-compat.patch: Cherry pick fix for Python 3.12 SSL
+    compatibility.
 
-python-oslo.service (3.4.0-2) unstable; urgency=medium
+ -- James Page <james.page@ubuntu.com>  Mon, 11 Mar 2024 11:49:55 +0000
 
-  * Uploading to unstable.
+python-oslo.service (3.3.0-0ubuntu1) noble; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Wed, 03 Apr 2024 16:45:07 +0200
+  [ Corey Bryant ]
+  * d/gbp.conf, .launchpad.yaml: Sync from cloud-archive-tools for
+    caracal.
 
-python-oslo.service (3.4.0-1) experimental; urgency=medium
+  [ James Page ]
+  * New upstream release for OpenStack Caracal.
 
-  * New upstream release.
+ -- James Page <james.page@ubuntu.com>  Wed, 17 Jan 2024 10:11:11 +0000
 
- -- Thomas Goirand <zigo@debian.org>  Sat, 24 Feb 2024 22:47:24 +0100
+python-oslo.service (3.1.1-0ubuntu2) lunar; urgency=medium
 
-python-oslo.service (3.2.0-3) unstable; urgency=medium
+  * d/p/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch,
+    d/p/0002-Revert-Profile-Oslo-Service-processes.patch: Restored.
+  * d/control: Drop python3-yappi inline with d/patches.
 
-  * Blacklist TestWSGIServerWithSSL.test_socket_options_for_ssl_server that is
-    failing. We don't care this, since in Debian, we're using uwsgi for the
-    SSL endpoint anyways.
+ -- Corey Bryant <corey.bryant@canonical.com>  Mon, 06 Mar 2023 08:05:07 -0500
 
- -- Thomas Goirand <zigo@debian.org>  Tue, 12 Dec 2023 09:13:24 +0100
+python-oslo.service (3.1.1-0ubuntu1) lunar; urgency=medium
 
-python-oslo.service (3.2.0-2) unstable; urgency=medium
+  * New upstream release for OpenStack Antelope.
+  * d/p/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch,
+    d/p/0002-Revert-Profile-Oslo-Service-processes.patch: Dropped. No
+    longer needed.
+  * d/control: Align (Build-)Depends with upstream.
 
-  * Uploading to unstable.
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 22 Feb 2023 14:23:01 -0500
 
- -- Thomas Goirand <zigo@debian.org>  Wed, 04 Oct 2023 14:08:13 +0200
+python-oslo.service (3.0.0-0ubuntu1) kinetic; urgency=medium
 
-python-oslo.service (3.2.0-1) experimental; urgency=medium
+  * New upstream release for OpenStack Zed.
+  * d/control: Update standards version to 4.6.1.
 
-  * New upstream release.
-  * Cleans even better.
+ -- Corey Bryant <corey.bryant@canonical.com>  Thu, 08 Sep 2022 20:27:38 -0400
 
- -- Thomas Goirand <zigo@debian.org>  Wed, 30 Aug 2023 15:13:38 +0200
+python-oslo.service (2.8.0-0ubuntu2) kinetic; urgency=medium
 
-python-oslo.service (3.1.1-3) unstable; urgency=medium
+  * d/t/control, d/t/python-(o)stestr: Switch from ostestr to stestr.
 
-  * Cleans better (Closes: #1045300).
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 08 Jun 2022 10:17:08 -0400
 
- -- Thomas Goirand <zigo@debian.org>  Tue, 15 Aug 2023 11:04:16 +0200
+python-oslo.service (2.8.0-0ubuntu1) jammy; urgency=medium
 
-python-oslo.service (3.1.1-2) unstable; urgency=medium
+  * New upstream release for OpenStack Yoga.
 
-  * Uploading to unstable.
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 12 Jan 2022 14:55:56 -0500
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 19 Jun 2023 11:44:35 +0200
+python-oslo.service (2.7.0-0ubuntu1) jammy; urgency=medium
 
-python-oslo.service (3.1.1-1) experimental; urgency=medium
+  * New upstream release for OpenStack Yoga.
+  * d/control: Bump debhelper compat to 13.
+  * d/p/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch,
+    d/p/0002-Revert-Profile-Oslo-Service-processes.patch: Rebased.
 
-  * New upstream release.
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 01 Dec 2021 15:46:22 -0500
 
- -- Thomas Goirand <zigo@debian.org>  Wed, 22 Feb 2023 11:25:23 +0100
+python-oslo.service (2.6.0-0ubuntu1) impish; urgency=medium
 
-python-oslo.service (3.0.0-2) unstable; urgency=medium
+  * New upstream release for OpenStack Xena.
+  * d/p/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch,
+    d/p/0002-Revert-Profile-Oslo-Service-processes.patch: Rebased.
 
-  * Uploading to unstable.
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 21 Jul 2021 09:43:22 -0400
 
- -- Thomas Goirand <zigo@debian.org>  Fri, 23 Sep 2022 13:57:25 +0200
+python-oslo.service (2.5.0-0ubuntu1) hirsute; urgency=medium
 
-python-oslo.service (3.0.0-1) experimental; urgency=medium
+  [ Chris MacNaughton ]
+  * d/control: Update VCS paths for move to lp:~ubuntu-openstack-dev.
 
-  * New upstream release.
+  [ Corey Bryant ]
+  * New upstream release for OpenStack Wallaby.
+  * d/p/no-patch-eventlet.patch: Dropped. Fixed in upstream release.
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 29 Aug 2022 16:48:54 +0200
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 17 Mar 2021 12:55:47 -0400
 
-python-oslo.service (2.8.0-2) unstable; urgency=medium
+python-oslo.service (2.4.0-0ubuntu2) hirsute; urgency=medium
 
-  * Uploading to unstable.
-  * Add autopkgtest.
+  * eventlet uses monotonic clocks by default since 0.21, hence no need to
+    patch that anymore.
 
- -- Thomas Goirand <zigo@debian.org>  Fri, 25 Mar 2022 09:07:16 +0100
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Mon, 30 Nov 2020 11:08:46 +0000
 
-python-oslo.service (2.8.0-1) experimental; urgency=medium
-
-  * New upstream release.
+python-oslo.service (2.4.0-0ubuntu1) groovy; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 21 Feb 2022 13:59:01 +0100
+  * New upstream release for OpenStack Victoria.
+  * d/control: Align (Build-)Depends with upstream.
 
-python-oslo.service (2.6.0-2) unstable; urgency=medium
+ -- Corey Bryant <corey.bryant@canonical.com>  Fri, 18 Sep 2020 12:36:07 -0400
 
-  * Uploading to unstable.
+python-oslo.service (2.3.2-0ubuntu1) groovy; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Wed, 29 Sep 2021 16:59:35 +0200
+  * New upstream release for OpenStack Victoria.
 
-python-oslo.service (2.6.0-1) experimental; urgency=medium
+ -- Chris MacNaughton <chris.macnaughton@canonical.com>  Thu, 03 Sep 2020 08:30:17 +0000
 
-  * New upstream release.
+python-oslo.service (2.3.1-0ubuntu1) groovy; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 23 Aug 2021 15:21:17 +0200
+  * New upstream release for OpenStack Victoria.
+  * d/control: Align (Build-)Depends with upstream.
+  * d/control, d/copyright, d/rules: Update upstream git URL.
 
-python-oslo.service (2.5.0-2) unstable; urgency=medium
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 29 Jul 2020 14:52:13 -0400
 
-  * Upload to unstable.
+python-oslo.service (2.2.0-0ubuntu1) groovy; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 16 Aug 2021 09:21:25 +0200
+  * New upstream release for OpenStack Victoria.
+  * d/control: Align (Build-)Depends with upstream.
+  * d/control, d/rules, d/compat: Switch to debhelper compat 12 and pybuild.
+  * d/p/monkey-patch-original-current-thread.patch: Dropped. Fixed upstream.
+  * d/control: Update Standards-Version to 4.5.0.
 
-python-oslo.service (2.5.0-1) experimental; urgency=medium
+ -- Corey Bryant <corey.bryant@canonical.com>  Thu, 11 Jun 2020 09:41:31 -0400
 
-  * New upstream release.
-  * Removed versions when satisfied in Bullseye.
+python-oslo.service (2.1.1-0ubuntu2) groovy; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 08 Mar 2021 09:29:36 +0100
+  * d/p/monkey-patch-original-current-thread.patch: Cherry-picked
+    from upstream review (https://review.opendev.org/#/c/725853/)
+    to fix Python 3.8 monkey patching (LP: #1863021).
 
-python-oslo.service (2.4.0-2) unstable; urgency=medium
+ -- Corey Bryant <corey.bryant@canonical.com>  Thu, 14 May 2020 12:56:30 -0400
 
-  * Uploading to unstable.
-  * Fixed debian/watch.
-  * Add a debian/salsa-ci.yml.
+python-oslo.service (2.1.1-0ubuntu1) focal; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Fri, 16 Oct 2020 10:12:25 +0200
+  * New upstream release for OpenStack Ussuri.
+  * d/control: Align (Build-)Depends with upstream.
 
-python-oslo.service (2.4.0-1) experimental; urgency=medium
+ -- Corey Bryant <corey.bryant@canonical.com>  Thu, 09 Apr 2020 15:30:40 -0400
 
-  * New upstream release.
+python-oslo.service (2.0.0-0ubuntu1) focal; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Sun, 13 Sep 2020 10:46:03 +0200
+  * New upstream release for OpenStack Ussuri.
+  * d/control: Align (Build-)Depends with upstream.
 
-python-oslo.service (2.3.2-1) experimental; urgency=medium
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 11 Mar 2020 16:02:55 -0400
 
-  * New upstream release.
-  * Fixed (build-)depends for this release.
-  * Removed call to python3 setup.py compile_catalog, as it's failing.
-  * Install using --in-tmp.
+python-oslo.service (1.41.1-0ubuntu1) focal; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Wed, 09 Sep 2020 21:37:46 +0200
+  * New upstream release for OpenStack Ussuri.
+  * d/control: Align (Build-)Depends with upstream.
 
-python-oslo.service (2.1.2-1) unstable; urgency=medium
+ -- Sahid Orentino Ferdjaoui <sahid.ferdjaoui@canonical.com>  Mon, 03 Feb 2020 11:00:11 +0000
 
-  * Run python3 setup.py compile_catalog before installing package, to make
-    sure translations are installed.
-  * New upstream release.
+python-oslo.service (1.40.2-0ubuntu2) focal; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 06 Jul 2020 11:11:24 +0200
+  * d/t/python-import: Ensure this test is py3-only.
 
-python-oslo.service (2.1.1-2) unstable; urgency=medium
+ -- Corey Bryant <corey.bryant@canonical.com>  Tue, 10 Dec 2019 13:21:03 -0500
 
-  * Uploading to unstable.
+python-oslo.service (1.40.2-0ubuntu1) eoan; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Fri, 08 May 2020 22:22:57 +0200
+  * New upstream release for OpenStack Train.
+  * d/p/*: Refresh/rebase.
 
-python-oslo.service (2.1.1-1) experimental; urgency=medium
+ -- James Page <james.page@ubuntu.com>  Thu, 26 Sep 2019 10:34:28 +0100
 
-  * New upstream release.
+python-oslo.service (1.40.0-0ubuntu2) eoan; urgency=medium
 
- -- Thomas Goirand <zigo@debian.org>  Tue, 07 Apr 2020 15:57:58 +0200
+  * d/control: Drop python3-yappi inline with d/patches.
 
-python-oslo.service (1.40.2-2) unstable; urgency=medium
+ -- Corey Bryant <corey.bryant@canonical.com>  Mon, 12 Aug 2019 15:23:15 -0400
 
-  [ Ondřej Nový ]
-  * Bump Standards-Version to 4.4.1.
+python-oslo.service (1.40.0-0ubuntu1) eoan; urgency=medium
 
-  [ Thomas Goirand ]
-  * Uploading to unstable.
+  * New upstream release for OpenStack Train.
+  * d/control: Align (Build-)Depends with upstream.
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 21 Oct 2019 00:50:29 +0200
+ -- Corey Bryant <corey.bryant@canonical.com>  Tue, 30 Jul 2019 15:48:44 -0400
 
-python-oslo.service (1.40.2-1) experimental; urgency=medium
+python-oslo.service (1.39.0-0ubuntu3) eoan; urgency=medium
 
-  * New upstream release.
+  * d/tests/*: Drop Python 2.7 tests.
 
- -- Thomas Goirand <zigo@debian.org>  Mon, 16 Sep 2019 11:37:32 +0200
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 26 Jun 2019 14:20:13 -0400
 
-python-oslo.service (1.40.1-1) unstable; urgency=medium
+python-oslo.service (1.39.0-0ubuntu2) eoan; urgency=medium
 
-  [ Ondřej Nový ]
-  * Use debhelper-compat instead of debian/compat.
-  * Bump Standards-Version to 4.4.0.
-
-  [ Thomas Goirand ]
-  * New upstream release.
+  * d/control, d/tests/*: Restore python-import test and drop
+    autopkgtest-pkg-python as it does not work with oslo packages.
 
- -- Thomas Goirand <zigo@debian.org>  Wed, 04 Sep 2019 14:31:23 +0200
+ -- Corey Bryant <corey.bryant@canonical.com>  Fri, 21 Jun 2019 10:23:58 -0400
 
-python-oslo.service (1.38.0-2) unstable; urgency=medium
+python-oslo.service (1.39.0-0ubuntu1) eoan; urgency=medium
 
-  * Uploading to unstable.
+  * Merge from Debian experimental.  Remaining changes:
+    - d/control: Enable autopkgtest-pkg-python testsuite.
+    - d/rules: Run all unit tests.
+    - d/t/python-ostestr: Run unit tests for dep8.
+    - d/gbp.conf: Retain for gbp and pristine-tar config.
+    - d/watch: Use upstream release tarballs.
+    - d/p/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch,
+      d/p/0002-Revert-Profile-Oslo-Service-processes.patch: 
+      Revert changes to support profiling with yappi.
+  * New upstream release for OpenStack Train.
+  * d/control: Align (Build-)Depends with upstream.
 
- -- Thomas Goirand <zigo@debian.org>  Wed, 17 Jul 2019 01:05:03 +0200
+ -- Corey Bryant <corey.bryant@canonical.com>  Fri, 07 Jun 2019 14:20:13 -0400
 
 python-oslo.service (1.38.0-1) experimental; urgency=medium
 
@@ -230,6 +271,35 @@ python-oslo.service (1.38.0-1) experimen
 
  -- Thomas Goirand <zigo@debian.org>  Thu, 21 Mar 2019 21:42:43 +0100
 
+python-oslo.service (1.38.0-0ubuntu1) disco; urgency=medium
+
+  * New upstream release for OpenStack Stein.
+  * d/control: Align (Build-)Depends with upstream.
+  * Revert changes to support profiling with yappi.
+
+ -- James Page <james.page@ubuntu.com>  Tue, 12 Mar 2019 09:12:35 +0000
+
+python-oslo.service (1.36.0-0ubuntu1) disco; urgency=medium
+
+  * New upstream release for OpenStack Stein.
+  * d/control: Align (Build-)Depends with upstream.
+  * d/tests/control: Drop use of needs-recommends.
+
+ -- James Page <james.page@ubuntu.com>  Wed, 30 Jan 2019 14:24:55 +0000
+
+python-oslo.service (1.33.0-0ubuntu2) disco; urgency=medium
+
+  * d/tests/python-ostestr: Skip test_child_signal_sighup for Python 3 due to
+    https://bugs.launchpad.net/bugs/1803731.
+
+ -- Corey Bryant <corey.bryant@canonical.com>  Fri, 16 Nov 2018 11:29:53 -0500
+
+python-oslo.service (1.33.0-0ubuntu1) disco; urgency=medium
+
+  * New upstream release for OpenStack Stein.
+
+ -- James Page <james.page@ubuntu.com>  Wed, 14 Nov 2018 15:27:19 +0000
+
 python-oslo.service (1.31.8-1) unstable; urgency=medium
 
   * New upstream release.
@@ -258,6 +328,36 @@ python-oslo.service (1.31.3-1) experimen
 
  -- Thomas Goirand <zigo@debian.org>  Mon, 20 Aug 2018 12:21:36 +0200
 
+python-oslo.service (1.31.3-0ubuntu1) cosmic; urgency=medium
+
+  * New upstream release for OpenStack Rocky.
+  * d/control: Align (Build-)Depends with upstream.
+
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 25 Jul 2018 13:58:56 -0400
+
+python-oslo.service (1.31.2-0ubuntu2) cosmic; urgency=medium
+
+  * d/tests/*,d/control: Add autopkgtests to exercise import and unit
+    tests as part of Ubuntu CI, resolving current failures.
+  * d/control: Switch to using python3-sphinx for doc build.
+
+ -- James Page <james.page@ubuntu.com>  Tue, 19 Jun 2018 10:15:56 +0100
+
+python-oslo.service (1.31.2-0ubuntu1) cosmic; urgency=low
+
+  [ Ubuntu Merge-o-Matic ]
+  * Merge from Debian unstable.  Remaining changes:
+    - d/watch: Use upstream release tarballs.
+    - d/rules: Rework unit test execution with blacklisting
+    - d/tests/*: Add autopkgtests.
+    - d/control: Update Vcs-* fields.
+
+  [ James Page ]
+  * New upstream release for OpenStack Rocky.
+  * Align (Build-)Depends with upstream.
+
+ -- James Page <james.page@ubuntu.com>  Wed, 13 Jun 2018 11:31:44 +0100
+
 python-oslo.service (1.29.0-2) unstable; urgency=medium
 
   * Uploading to unstable.
@@ -273,6 +373,29 @@ python-oslo.service (1.29.0-1) experimen
 
  -- Thomas Goirand <zigo@debian.org>  Sun, 11 Feb 2018 13:00:00 +0000
 
+python-oslo.service (1.29.0-0ubuntu1) bionic; urgency=medium
+
+  * New upstream release for OpenStack Queens.
+  * d/control: Align (Build-)Depends with upstream.
+
+ -- James Page <james.page@ubuntu.com>  Fri, 26 Jan 2018 10:49:00 +0000
+
+python-oslo.service (1.28.0-0ubuntu1) bionic; urgency=medium
+
+  * New upstream release for OpenStack Queens.
+  * d/control: Align (Build-)Depends with upstream.
+  * d/p/drop-openstackdoctheme.patch: Drop, no longer required.
+  * d/control: Bumped Standards-Version to 4.1.2.
+
+ -- James Page <james.page@ubuntu.com>  Fri, 08 Dec 2017 11:23:45 +0000
+
+python-oslo.service (1.27.0-0ubuntu1) bionic; urgency=medium
+
+  * New upstream release.
+  * d/control: Align (Build-)Depends with upstream.
+
+ -- James Page <james.page@ubuntu.com>  Mon, 13 Nov 2017 17:17:19 +0000
+
 python-oslo.service (1.25.0-2) unstable; urgency=medium
 
   * Uploading to unstable:
@@ -313,6 +436,75 @@ python-oslo.service (1.25.0-1) experimen
 
  -- Thomas Goirand <zigo@debian.org>  Tue, 19 Sep 2017 19:47:05 +0000
 
+python-oslo.service (1.25.0-0ubuntu1) artful; urgency=medium
+
+  * New upstream release.
+  * d/control: Align (Build-)Depends with upstream.
+  * d/p/drop-openstackdoctheme.patch: Temporarily drop openstackdocstheme
+    sphinx extension until sphinx>=1.6.2 is available.
+
+ -- Corey Bryant <corey.bryant@canonical.com>  Fri, 11 Aug 2017 18:26:12 -0400
+
+python-oslo.service (1.24.0-0ubuntu1) artful; urgency=medium
+
+  * New upstream release.
+  * d/control: Align (Build-)Depends with upstream.
+
+ -- Corey Bryant <corey.bryant@canonical.com>  Fri, 07 Jul 2017 12:13:14 -0400
+
+python-oslo.service (1.23.0-0ubuntu1) artful; urgency=medium
+
+  * New upstream release.
+  * d/control: Align (Build-)Depends with upstream.
+  * d/rules,control,test-blacklist-py*.txt: Switch to using ostestr for
+    unit test execution, rework blacklisted tests into files.
+
+ -- James Page <james.page@ubuntu.com>  Fri, 02 Jun 2017 09:26:16 +0100
+
+python-oslo.service (1.22.0-0ubuntu1) artful; urgency=medium
+
+  [ Chuck Short ]
+  * New upstream release.
+  * debian/control: Bump version depedencies.
+
+  [ James Page ]
+  * d/watch: Use tarballs.openstack.org.
+  * New upstream release.
+  * d/control: Align (Build-)Depends with upstream.
+
+ -- James Page <james.page@ubuntu.com>  Fri, 21 Apr 2017 10:04:44 +0100
+
+python-oslo.service (1.19.0-0ubuntu1) zesty; urgency=medium
+
+  * New upstream release.
+
+ -- Corey Bryant <corey.bryant@canonical.com>  Thu, 19 Jan 2017 12:26:14 -0500
+
+python-oslo.service (1.18.0-0ubuntu1) zesty; urgency=medium
+
+  * New upstream version.
+  * debian/control: Bump version depedencies.
+
+ -- Chuck Short <zulcss@ubuntu.com>  Fri, 02 Dec 2016 10:14:48 -0500
+
+python-oslo.service (1.17.0-1ubuntu2) zesty; urgency=medium
+
+  * debian/tests: Add autopkgtest 
+
+ -- Chuck Short <zulcss@ubuntu.com>  Thu, 03 Nov 2016 09:17:39 -0400
+
+python-oslo.service (1.17.0-1ubuntu1) zesty; urgency=medium
+
+  [ Corey Bryant ]
+  * d/gbp.conf: Update gbp configuration file.
+  * d/control: Update Vcs-* links and maintainers.
+
+  [ Chuck Short ]
+  * New upstream version.
+  * debian/control: Bump version depedencies.
+
+ -- Chuck Short <zulcss@ubuntu.com>  Tue, 25 Oct 2016 15:04:42 -0400
+
 python-oslo.service (1.16.0-2) unstable; urgency=medium
 
   [ Ondřej Nový ]
@@ -336,6 +528,17 @@ python-oslo.service (1.16.0-1) experimen
 
  -- Thomas Goirand <zigo@debian.org>  Mon, 12 Sep 2016 12:56:22 +0200
 
+python-oslo.service (1.16.0-0ubuntu1) yakkety; urgency=medium
+
+  [ Corey Bryant ]
+  * New upstream release.
+  * d/control: Align (Build-)Depends with upstream.
+
+  [ Corey Bryant ]
+  * New upstream release.
+
+ -- Corey Bryant <corey.bryant@canonical.com>  Wed, 31 Aug 2016 14:04:15 -0400
+
 python-oslo.service (1.11.0-1) experimental; urgency=medium
 
   [ Corey Bryant ]
@@ -438,3 +641,4 @@ python-oslo.service (0.1.0-1) experiment
   * Initial release. (Closes: #789920)
 
  -- Thomas Goirand <zigo@debian.org>  Thu, 25 Jun 2015 10:16:19 +0200
+
diff -pruN 4.1.1-2/debian/control 4.2.2-0ubuntu1/debian/control
--- 4.1.1-2/debian/control	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/control	2025-08-20 09:16:35.000000000 +0000
@@ -1,48 +1,48 @@
 Source: python-oslo.service
 Section: python
 Priority: optional
-Maintainer: Debian OpenStack <team+openstack@tracker.debian.org>
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+XSBC-Original-Maintainer: Debian OpenStack <team+openstack@tracker.debian.org>
 Uploaders:
  Thomas Goirand <zigo@debian.org>,
  Corey Bryant <corey.bryant@canonical.com>,
 Build-Depends:
- debhelper-compat (= 10),
+ debhelper-compat (= 13),
  dh-python,
  openstack-pkg-tools,
  python3-all,
  python3-pbr,
  python3-setuptools,
- python3-sphinx,
+ python3-sphinx (>= 2.0.0),
 Build-Depends-Indep:
  procps,
- python3-babel,
- python3-coverage,
- python3-debtcollector,
- python3-eventlet,
- python3-fixtures,
- python3-greenlet,
- python3-hacking,
- python3-openstackdocstheme,
- python3-oslo.concurrency,
- python3-oslo.config,
- python3-oslo.i18n,
- python3-oslo.log,
- python3-oslo.utils,
+ python3-bandit,
+ python3-cotyledon (>= 1.7.3),
+ python3-debtcollector (>= 1.2.0),
+ python3-eventlet (>= 0.27.0),
+ python3-fixtures (>= 3.0.0),
+ python3-futurist (>= 2.4.1),
+ python3-greenlet (>= 0.4.15),
+ python3-openstackdocstheme (>= 2.2.0),
+ python3-oslo.concurrency (>= 3.25.0),
+ python3-oslo.config (>= 1:5.1.0),
+ python3-oslo.i18n (>= 3.15.3),
+ python3-oslo.log (>= 3.36.0),
+ python3-oslo.utils (>= 3.40.2),
  python3-oslosphinx,
- python3-oslotest,
- python3-paste,
- python3-pastedeploy,
- python3-requests,
- python3-routes,
- python3-stestr,
+ python3-oslotest (>= 1:3.2.0),
+ python3-paste (>= 2.0.2),
+ python3-pastedeploy (>= 1.5.0),
+ python3-requests (>= 2.14.2),
+ python3-routes (>= 2.3.1),
+ python3-stestr (>= 2.0.0),
  python3-subunit,
- python3-webob,
- python3-yappi,
+ python3-webob (>= 1:1.7.1),
+ python3-yappi (>= 1.0),
  subunit,
-Standards-Version: 4.4.1
-Vcs-Browser: https://salsa.debian.org/openstack-team/oslo/python-oslo.service
-Vcs-Git: https://salsa.debian.org/openstack-team/oslo/python-oslo.service.git
-Homepage: https://github.com/openstack/oslo.service
+Standards-Version: 4.6.1
+Vcs-Git: https://git.launchpad.net/~ubuntu-openstack-dev/ubuntu/+source/python-oslo.service
+Homepage: https://opendev.org/openstack/oslo.service
 
 Package: python-oslo.service-doc
 Section: doc
@@ -67,18 +67,21 @@ Architecture: all
 Depends:
  procps,
  python3-babel,
- python3-debtcollector,
- python3-eventlet,
- python3-fixtures,
- python3-oslo.concurrency,
- python3-oslo.config,
- python3-oslo.i18n,
- python3-oslo.log,
- python3-oslo.utils,
- python3-paste,
- python3-pastedeploy,
- python3-routes,
- python3-yappi,
+ python3-cotyledon (>= 1.7.3),
+ python3-debtcollector (>= 1.2.0),
+ python3-eventlet (>= 0.27.0),
+ python3-fixtures (>= 3.0.0),
+ python3-futurist (>= 2.4.1),
+ python3-greenlet (>= 0.4.15),
+ python3-oslo.concurrency (>= 3.25.0),
+ python3-oslo.config (>= 1:5.1.0),
+ python3-oslo.i18n (>= 3.15.3),
+ python3-oslo.log (>= 3.36.0),
+ python3-oslo.utils (>= 3.40.2),
+ python3-paste (>= 2.0.2),
+ python3-pastedeploy (>= 1.5.0),
+ python3-routes (>= 2.3.1),
+ python3-webob (>= 1:1.7.1),
  ${misc:Depends},
  ${python3:Depends},
 Suggests:
diff -pruN 4.1.1-2/debian/copyright 4.2.2-0ubuntu1/debian/copyright
--- 4.1.1-2/debian/copyright	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/copyright	2025-08-20 09:16:35.000000000 +0000
@@ -1,6 +1,6 @@
 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Name: oslo.service
-Source: https://github.com/openstack/oslo.service
+Source: https://opendev.org/openstack/oslo.service
 
 Files: *
 Copyright: (c) 2015-2016, OpenStack Foundation <openstack-dev@lists.openstack.org>
diff -pruN 4.1.1-2/debian/gbp.conf 4.2.2-0ubuntu1/debian/gbp.conf
--- 4.1.1-2/debian/gbp.conf	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/gbp.conf	2025-08-20 09:16:35.000000000 +0000
@@ -0,0 +1,9 @@
+[DEFAULT]
+debian-branch = master
+upstream-tag = %(version)s
+pristine-tar = True
+upstream-branch = upstream-flamingo
+
+[buildpackage]
+export-dir = ../build-area
+prebuild = [ ! -f .launchpad.yaml ] || rm .launchpad.yaml
diff -pruN 4.1.1-2/debian/patches/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch 4.2.2-0ubuntu1/debian/patches/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch
--- 4.1.1-2/debian/patches/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/patches/0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch	2025-08-20 09:16:35.000000000 +0000
@@ -0,0 +1,22 @@
+From 8eb77cf7b8ff877424dda30ddd2ff338db90d359 Mon Sep 17 00:00:00 2001
+From: James Page <james.page@ubuntu.com>
+Date: Mon, 11 Mar 2019 16:31:19 +0000
+Subject: [PATCH 1/2] Revert "Update oslo.service to require yappi 1.0 or
+ newer"
+
+This reverts commit ca6f839fc49e8e57995d4e107ee3f428f9baa1e3.
+---
+ lower-constraints.txt | 2 +-
+ requirements.txt      | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+Index: python-oslo.service/requirements.txt
+===================================================================
+--- python-oslo.service.orig/requirements.txt
++++ python-oslo.service/requirements.txt
+@@ -14,4 +14,4 @@ oslo.i18n>=3.15.3 # Apache-2.0
+ PasteDeploy>=1.5.0 # MIT
+ Routes>=2.3.1 # MIT
+ Paste>=2.0.2 # MIT
+-Yappi>=1.0 # MIT
++Yappi>=0.98 # MIT
diff -pruN 4.1.1-2/debian/patches/0002-Revert-Profile-Oslo-Service-processes.patch 4.2.2-0ubuntu1/debian/patches/0002-Revert-Profile-Oslo-Service-processes.patch
--- 4.1.1-2/debian/patches/0002-Revert-Profile-Oslo-Service-processes.patch	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/patches/0002-Revert-Profile-Oslo-Service-processes.patch	2025-08-20 09:16:35.000000000 +0000
@@ -0,0 +1,136 @@
+From e254491b03e906ff427d0aabfb06c5b82b34c65f Mon Sep 17 00:00:00 2001
+From: James Page <james.page@ubuntu.com>
+Date: Mon, 11 Mar 2019 16:31:29 +0000
+Subject: [PATCH 2/2] Revert "Profile Oslo Service processes"
+
+This reverts commit a04daefbb158e955dcfe7379e2b38c272ff31da2.
+---
+ doc/source/user/usage.rst                     | 37 -------------------
+ lower-constraints.txt                         |  1 -
+ oslo_service/eventlet_backdoor.py             | 27 --------------
+ .../profile-worker-5d3fd0f0251d62b8.yaml      |  5 ---
+ requirements.txt                              |  1 -
+ 5 files changed, 71 deletions(-)
+ delete mode 100644 releasenotes/notes/profile-worker-5d3fd0f0251d62b8.yaml
+
+Index: python-oslo.service/doc/source/user/usage.rst
+===================================================================
+--- python-oslo.service.orig/doc/source/user/usage.rst
++++ python-oslo.service/doc/source/user/usage.rst
+@@ -198,41 +198,3 @@ logging options by sending a ``SIGHUP``.
+         def reset(self):
+             logging.setup(cfg.CONF, 'foo')
+ 
+-
+-Profiling
+-~~~~~~~~~
+-
+-Processes spawned through :mod:`oslo_service.service` can be profiled (function
+-calltrace) through the :mod:`~oslo_service.eventlet_backdoor` module. The
+-service must be configured with the :oslo.config:option:`backdoor_port` option
+-to enable its workers to listen on TCP ports. The user can then send the
+-``prof()`` command to capture the worker process's function calltrace.
+-
+-1) To start profiling send the ``prof()`` command on the process's listening
+-   port
+-
+-2) To stop profiling and capture pstat calltrace to a file, send the
+-   ``prof()`` command with a file basename as an argument (``prof(basename)``)
+-   to the worker process's listening port. A stats file (in pstat format) will
+-   be generated in the temp directory with the user-provided basename with a
+-   ``.prof`` suffix .
+-
+-For example, to profile a neutron server process (which is listening on
+-port 8002 configured through the :oslo.config:option:`backdoor_port` option):
+-
+-.. code-block:: bash
+-
+-    $ echo "prof()" | nc localhost 8002
+-    $ neutron net-create n1; neutron port-create --name p1 n1;
+-    $ neutron port-delete p1; neutron port-delete p1
+-    $ echo "prof('neutron')" | nc localhost 8002
+-
+-This will generate a stats file in ``/tmp/neutron.prof``. Stats can be printed
+-from the trace file as follows:
+-
+-.. code-block:: python
+-
+-    import pstats
+-
+-    stats = pstats.Stats('/tmp/neutron.prof')
+-    stats.print_stats()
+Index: python-oslo.service/oslo_service/eventlet_backdoor.py
+===================================================================
+--- python-oslo.service.orig/oslo_service/eventlet_backdoor.py
++++ python-oslo.service/oslo_service/eventlet_backdoor.py
+@@ -20,12 +20,10 @@ import logging
+ import os
+ import pprint
+ import sys
+-import tempfile
+ import traceback
+ 
+ import eventlet.backdoor
+ import greenlet
+-import yappi
+ 
+ from eventlet.green import socket
+ from oslo_service._i18n import _
+@@ -104,30 +102,6 @@ def _find_objects(t):
+             if hasattr(o, "__class__") and isinstance(o, t)]
+ 
+ 
+-def _capture_profile(fname=''):
+-    if not fname:
+-        yappi.set_clock_type('cpu')
+-        # We need to set context to greenlet to profile greenlets
+-        # https://bitbucket.org/sumerc/yappi/pull-requests/3
+-        yappi.set_context_id_callback(
+-            lambda: id(greenlet.getcurrent()))
+-        yappi.set_context_name_callback(
+-            lambda: greenlet.getcurrent().__class__.__name__)
+-        yappi.start()
+-    else:
+-        yappi.stop()
+-        stats = yappi.get_func_stats()
+-        # User should provide filename. This file with a suffix .prof
+-        # will be created in temp directory.
+-        try:
+-            stats_file = os.path.join(tempfile.gettempdir(), fname + '.prof')
+-            stats.save(stats_file, "pstat")
+-        except Exception as e:
+-            print("Error while saving the trace stats ", str(e))
+-        finally:
+-            yappi.clear_stats()
+-
+-
+ def _print_greenthreads(simple=True):
+     for i, gt in enumerate(_find_objects(greenlet.greenlet)):
+         print(i, gt)
+@@ -214,7 +188,6 @@ def _initialize_if_enabled(conf):
+         'fo': _find_objects,
+         'pgt': _print_greenthreads,
+         'pnt': _print_nativethreads,
+-        'prof': _capture_profile,
+     }
+ 
+     if conf.backdoor_port is None and conf.backdoor_socket is None:
+Index: python-oslo.service/releasenotes/notes/profile-worker-5d3fd0f0251d62b8.yaml
+===================================================================
+--- python-oslo.service.orig/releasenotes/notes/profile-worker-5d3fd0f0251d62b8.yaml
++++ /dev/null
+@@ -1,5 +0,0 @@
+----
+-features:
+-  - |
+-    Add support for profiling (capture function calltrace) service's worker
+-    processes.
+Index: python-oslo.service/requirements.txt
+===================================================================
+--- python-oslo.service.orig/requirements.txt
++++ python-oslo.service/requirements.txt
+@@ -14,4 +14,3 @@ oslo.i18n>=3.15.3 # Apache-2.0
+ PasteDeploy>=1.5.0 # MIT
+ Routes>=2.3.1 # MIT
+ Paste>=2.0.2 # MIT
+-Yappi>=0.98 # MIT
diff -pruN 4.1.1-2/debian/patches/series 4.2.2-0ubuntu1/debian/patches/series
--- 4.1.1-2/debian/patches/series	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/patches/series	2025-08-20 09:16:35.000000000 +0000
@@ -0,0 +1,2 @@
+0001-Revert-Update-oslo.service-to-require-yappi-1.0-or-n.patch
+0002-Revert-Profile-Oslo-Service-processes.patch
diff -pruN 4.1.1-2/debian/python3-oslo.service.install 4.2.2-0ubuntu1/debian/python3-oslo.service.install
--- 4.1.1-2/debian/python3-oslo.service.install	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/python3-oslo.service.install	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-/usr
diff -pruN 4.1.1-2/debian/rules 4.2.2-0ubuntu1/debian/rules
--- 4.1.1-2/debian/rules	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/rules	2025-08-20 09:16:35.000000000 +0000
@@ -1,24 +1,25 @@
 #!/usr/bin/make -f
 
-UPSTREAM_GIT := https://github.com/openstack/oslo.service.git
+export PYBUILD_NAME=oslo_service
+
+UPSTREAM_GIT := https://opendev.org/openstack/oslo.service.git
 include /usr/share/openstack-pkg-tools/pkgos.make
 
 %:
 	dh $@ --buildsystem=pybuild --with python3,sphinxdoc
 
 override_dh_auto_clean:
-	rm -rf build *.egg-info
-	find .  -type d -name __pycache__ -exec rm -r {} \+
+	rm -rf build
 
 override_dh_auto_build:
 	echo "Do nothing..."
 
 override_dh_auto_install:
-	pkgos-dh_auto_install --no-py2 --in-tmp
+	pkgos-dh_auto_install --no-py2
 
 override_dh_auto_test:
 ifeq (,$(findstring nocheck, $(DEB_BUILD_OPTIONS)))
-	pkgos-dh_auto_test --no-py2 'oslo_service\.tests\.(?!(.*test_wsgi\.TestWSGIServerWithSSL\.test_app_using_ipv6_and_ssl.*|.*test_wsgi\.TestWSGIServerWithSSL\.test_ssl_server.*|.*test_wsgi\.TestWSGIServerWithSSL\.test_two_servers.*|.*test_service\.EventletServerServiceLauncherTest\.test_graceful_stop_with_exceeded_graceful_shutdown_timeout.*|.*test_service.EventletServerServiceLauncherTest.test_shuts_down_on_sigint_when_client_connected.*|.*test_service.ServiceLauncherTest.test_child_signal_sighup.*|.*test_service.ProcessLauncherTest.test_stop.*|.*test_wsgi\.TestWSGIServerWithSSL\.test_socket_options_for_ssl_server.*))'
+	pkgos-dh_auto_test --no-py2
 endif
 
 override_dh_sphinxdoc:
diff -pruN 4.1.1-2/debian/salsa-ci.yml 4.2.2-0ubuntu1/debian/salsa-ci.yml
--- 4.1.1-2/debian/salsa-ci.yml	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/salsa-ci.yml	1970-01-01 00:00:00.000000000 +0000
@@ -1,6 +0,0 @@
-include:
-  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
-  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
-
-variables:
-  SALSA_CI_DISABLE_AUTOPKGTEST: 1
diff -pruN 4.1.1-2/debian/source/options 4.2.2-0ubuntu1/debian/source/options
--- 4.1.1-2/debian/source/options	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/source/options	2025-08-20 09:16:35.000000000 +0000
@@ -1 +1,2 @@
 extend-diff-ignore = "^[^/]*[.]egg-info/"
+extend-diff-ignore = "^.launchpad.yaml"
diff -pruN 4.1.1-2/debian/tests/control 4.2.2-0ubuntu1/debian/tests/control
--- 4.1.1-2/debian/tests/control	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/tests/control	2025-08-20 09:16:35.000000000 +0000
@@ -1,5 +1,3 @@
-Tests: unittests
-Depends:
- @,
- @builddeps@,
-Restrictions: allow-stderr needs-root
+Tests: python-import, python-stestr
+Depends: @, @builddeps@, python3-all
+Restrictions: allow-stderr
diff -pruN 4.1.1-2/debian/tests/python-import 4.2.2-0ubuntu1/debian/tests/python-import
--- 4.1.1-2/debian/tests/python-import	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/tests/python-import	2025-08-20 09:16:35.000000000 +0000
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+set -e
+
+MODULE_NAME=$(python3 setup.py --name | sed 's/\./_/g')
+
+for py in $(py3versions --supported 2>/dev/null) ; do
+    cd "$AUTOPKGTEST_TMP"
+    echo "Testing with $py:"
+    $py -c "import $MODULE_NAME; print($MODULE_NAME)"
+done
diff -pruN 4.1.1-2/debian/tests/python-stestr 4.2.2-0ubuntu1/debian/tests/python-stestr
--- 4.1.1-2/debian/tests/python-stestr	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/tests/python-stestr	2025-08-20 09:16:35.000000000 +0000
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+set -ex
+
+for py in $(py3versions --supported 2>/dev/null); do
+    echo "Testing with $py:"
+    PYTHON=$py stestr run
+    rm -rf .stestr
+done
diff -pruN 4.1.1-2/debian/tests/unittests 4.2.2-0ubuntu1/debian/tests/unittests
--- 4.1.1-2/debian/tests/unittests	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/tests/unittests	1970-01-01 00:00:00.000000000 +0000
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-set -e
-
-pkgos-dh_auto_test --no-py2 'oslo_service\.tests\.(?!(.*test_wsgi\.TestWSGIServerWithSSL\.test_app_using_ipv6_and_ssl.*|.*test_wsgi\.TestWSGIServerWithSSL\.test_ssl_server.*|.*test_wsgi\.TestWSGIServerWithSSL\.test_two_servers.*|.*test_service\.EventletServerServiceLauncherTest\.test_graceful_stop_with_exceeded_graceful_shutdown_timeout.*|.*test_service.EventletServerServiceLauncherTest.test_shuts_down_on_sigint_when_client_connected.*|.*test_service.ServiceLauncherTest.test_child_signal_sighup.*|.*test_service.ProcessLauncherTest.test_stop.*|.*test_wsgi\.TestWSGIServerWithSSL\.test_socket_options_for_ssl_server.*))'
diff -pruN 4.1.1-2/debian/watch 4.2.2-0ubuntu1/debian/watch
--- 4.1.1-2/debian/watch	2025-03-28 09:36:58.000000000 +0000
+++ 4.2.2-0ubuntu1/debian/watch	2025-08-20 09:16:35.000000000 +0000
@@ -1,3 +1,3 @@
-version=4
-opts="mode=git,uversionmangle=s/\.0rc/~rc/;s/\.0b1/~b1/;s/\.0b2/~b2/;s/\.0b3/~b3/" \
-https://github.com/openstack/oslo.service refs/tags/(\d[brc\d\.]+)
+version=3
+opts="uversionmangle=s/\.(b|rc)/~$1/" \
+    http://tarballs.openstack.org/oslo.service/ oslo.service-(\d.*)\.tar\.gz
diff -pruN 4.1.1-2/oslo.service.egg-info/PKG-INFO 4.2.2-0ubuntu1/oslo.service.egg-info/PKG-INFO
--- 4.1.1-2/oslo.service.egg-info/PKG-INFO	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo.service.egg-info/PKG-INFO	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1,72 @@
+Metadata-Version: 2.1
+Name: oslo.service
+Version: 4.2.2
+Summary: oslo.service library
+Home-page: https://docs.openstack.org/oslo.service/latest/
+Author: OpenStack
+Author-email: openstack-discuss@lists.openstack.org
+Classifier: Environment :: OpenStack
+Classifier: Intended Audience :: Information Technology
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Requires-Python: >=3.9
+License-File: LICENSE
+Requires-Dist: WebOb>=1.7.1
+Requires-Dist: debtcollector>=1.2.0
+Requires-Dist: eventlet>=0.27.0
+Requires-Dist: greenlet>=0.4.15
+Requires-Dist: oslo.utils>=3.40.2
+Requires-Dist: oslo.concurrency>=3.25.0
+Requires-Dist: oslo.config>=5.1.0
+Requires-Dist: oslo.log>=3.36.0
+Requires-Dist: oslo.i18n>=3.15.3
+Requires-Dist: PasteDeploy>=1.5.0
+Requires-Dist: Routes>=2.3.1
+Requires-Dist: Paste>=2.0.2
+Requires-Dist: Yappi>=1.0
+Provides-Extra: threading
+Requires-Dist: cotyledon>=2.0.0; extra == "threading"
+Requires-Dist: futurist>=3.1.1; extra == "threading"
+
+========================
+Team and repository tags
+========================
+
+.. image:: https://governance.openstack.org/tc/badges/oslo.service.svg
+    :target: https://governance.openstack.org/tc/ference/tags/index.html
+
+.. Change things from this point on
+
+========================================================
+ oslo.service -- Library for running OpenStack services
+========================================================
+
+.. image:: https://img.shields.io/pypi/v/oslo.service.svg
+    :target: https://pypi.org/project/oslo.service/
+    :alt: Latest Version
+
+.. image:: https://img.shields.io/pypi/dm/oslo.service.svg
+    :target: https://pypi.org/project/oslo.service/
+    :alt: Downloads
+
+oslo.service provides a framework for defining new long-running
+services using the patterns established by other OpenStack
+applications. It also includes utilities long-running applications
+might need for working with SSL or WSGI, performing periodic
+operations, interacting with systemd, etc.
+
+* Free software: Apache license
+* Documentation: https://docs.openstack.org/oslo.service/latest/
+* Source: https://opendev.org/openstack/oslo.service
+* Bugs: https://bugs.launchpad.net/oslo.service
+* Release notes: https://docs.openstack.org/releasenotes/oslo.service/
+
diff -pruN 4.1.1-2/oslo.service.egg-info/SOURCES.txt 4.2.2-0ubuntu1/oslo.service.egg-info/SOURCES.txt
--- 4.1.1-2/oslo.service.egg-info/SOURCES.txt	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo.service.egg-info/SOURCES.txt	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1,139 @@
+.coveragerc
+.mailmap
+.pre-commit-config.yaml
+.stestr.conf
+.zuul.yaml
+AUTHORS
+CONTRIBUTING.rst
+ChangeLog
+HACKING.rst
+LICENSE
+README.rst
+pyproject.toml
+requirements.txt
+setup.cfg
+setup.py
+test-requirements.txt
+tox.ini
+doc/requirements.txt
+doc/source/conf.py
+doc/source/index.rst
+doc/source/configuration/index.rst
+doc/source/contributor/index.rst
+doc/source/install/index.rst
+doc/source/reference/eventlet_backdoor.rst
+doc/source/reference/fixture.rst
+doc/source/reference/index.rst
+doc/source/reference/loopingcall.rst
+doc/source/reference/periodic_task.rst
+doc/source/reference/service.rst
+doc/source/reference/sslutils.rst
+doc/source/reference/systemd.rst
+doc/source/reference/threadgroup.rst
+doc/source/user/history.rst
+doc/source/user/index.rst
+doc/source/user/usage.rst
+oslo.service.egg-info/PKG-INFO
+oslo.service.egg-info/SOURCES.txt
+oslo.service.egg-info/dependency_links.txt
+oslo.service.egg-info/entry_points.txt
+oslo.service.egg-info/not-zip-safe
+oslo.service.egg-info/pbr.json
+oslo.service.egg-info/requires.txt
+oslo.service.egg-info/top_level.txt
+oslo_service/__init__.py
+oslo_service/_i18n.py
+oslo_service/_options.py
+oslo_service/eventlet_backdoor.py
+oslo_service/fixture.py
+oslo_service/loopingcall.py
+oslo_service/periodic_task.py
+oslo_service/service.py
+oslo_service/sslutils.py
+oslo_service/systemd.py
+oslo_service/threadgroup.py
+oslo_service/version.py
+oslo_service/wsgi.py
+oslo_service/backend/__init__.py
+oslo_service/backend/base.py
+oslo_service/backend/exceptions.py
+oslo_service/backend/common/__init__.py
+oslo_service/backend/common/constants.py
+oslo_service/backend/common/daemon_utils.py
+oslo_service/backend/common/signal_utils.py
+oslo_service/backend/common/singleton.py
+oslo_service/backend/common/validation_utils.py
+oslo_service/backend/eventlet/__init__.py
+oslo_service/backend/eventlet/loopingcall.py
+oslo_service/backend/eventlet/service.py
+oslo_service/backend/eventlet/threadgroup.py
+oslo_service/backend/threading/__init__.py
+oslo_service/backend/threading/loopingcall.py
+oslo_service/backend/threading/service.py
+oslo_service/backend/threading/threadgroup.py
+oslo_service/locale/en_GB/LC_MESSAGES/oslo_service.po
+oslo_service/tests/__init__.py
+oslo_service/tests/base.py
+oslo_service/tests/eventlet_service.py
+oslo_service/tests/test_eventlet_backdoor.py
+oslo_service/tests/test_fixture.py
+oslo_service/tests/test_loopingcall.py
+oslo_service/tests/test_periodic.py
+oslo_service/tests/test_service.py
+oslo_service/tests/test_sslutils.py
+oslo_service/tests/test_systemd.py
+oslo_service/tests/test_threadgroup.py
+oslo_service/tests/test_wsgi.py
+oslo_service/tests/backend/__init__.py
+oslo_service/tests/backend/tests/__init__.py
+oslo_service/tests/backend/tests/test_backend_base.py
+oslo_service/tests/backend/tests/test_backend_init.py
+oslo_service/tests/backend/threading/__init__.py
+oslo_service/tests/backend/threading/test_launcher.py
+oslo_service/tests/ssl_cert/ca.crt
+oslo_service/tests/ssl_cert/ca.key
+oslo_service/tests/ssl_cert/certificate.crt
+oslo_service/tests/ssl_cert/privatekey.key
+releasenotes/notes/accept-wait-interval-threading-fc02f76056cbaed4.yaml
+releasenotes/notes/add-backend-system-eventlet-migration-deprecations-da46d5a70d7f057d.yaml
+releasenotes/notes/add-threading-backend-9b0e601e5c1282e1.yaml
+releasenotes/notes/add-timeout-looping-call-5cc396b75597c3c2.yaml
+releasenotes/notes/add-wsgi_server_debug-opt-70d818b5b78bfc7c.yaml
+releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml
+releasenotes/notes/drop-python27-support-1cfdf65193a03f3a.yaml
+releasenotes/notes/fix-find-object-in-backdoor-487bf78c4c502594.yaml
+releasenotes/notes/fix-threading-shutdown-cotyledon-3aff6ca0eb0125f5.yaml
+releasenotes/notes/native-threads-on-child-7150690c7caa1013.yaml
+releasenotes/notes/profile-worker-5d3fd0f0251d62b8.yaml
+releasenotes/notes/remove-deprecated-ssl-opts-46e9d7a53f725340.yaml
+releasenotes/notes/remove-py38-c69fe0003b2c49cb.yaml
+releasenotes/notes/service-children-SIGHUP-15d0cf6d2a1bdbf9.yaml
+releasenotes/notes/support-pid-in-eventlet-backdoor-socket-path-1863eaad1dd08556.yaml
+releasenotes/notes/threadgroup-cancel-bd89d72f383a3d9b.yaml
+releasenotes/notes/timer-args-f578c8f9d08b217d.yaml
+releasenotes/notes/timer-stop_on_exception-9f21d7c4d6d1b0d9.yaml
+releasenotes/notes/undeprecate-wsgi-7e5306403625b8c6.yaml
+releasenotes/source/2023.1.rst
+releasenotes/source/2023.2.rst
+releasenotes/source/2024.1.rst
+releasenotes/source/2024.2.rst
+releasenotes/source/2025.1.rst
+releasenotes/source/conf.py
+releasenotes/source/index.rst
+releasenotes/source/ocata.rst
+releasenotes/source/pike.rst
+releasenotes/source/queens.rst
+releasenotes/source/rocky.rst
+releasenotes/source/stein.rst
+releasenotes/source/train.rst
+releasenotes/source/unreleased.rst
+releasenotes/source/ussuri.rst
+releasenotes/source/victoria.rst
+releasenotes/source/wallaby.rst
+releasenotes/source/xena.rst
+releasenotes/source/yoga.rst
+releasenotes/source/zed.rst
+releasenotes/source/_static/.placeholder
+releasenotes/source/_templates/.placeholder
+releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po
+releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po
\ No newline at end of file
diff -pruN 4.1.1-2/oslo.service.egg-info/dependency_links.txt 4.2.2-0ubuntu1/oslo.service.egg-info/dependency_links.txt
--- 4.1.1-2/oslo.service.egg-info/dependency_links.txt	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo.service.egg-info/dependency_links.txt	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1 @@
+
diff -pruN 4.1.1-2/oslo.service.egg-info/entry_points.txt 4.2.2-0ubuntu1/oslo.service.egg-info/entry_points.txt
--- 4.1.1-2/oslo.service.egg-info/entry_points.txt	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo.service.egg-info/entry_points.txt	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1,5 @@
+[oslo.config.opts]
+oslo.service.periodic_task = oslo_service.periodic_task:list_opts
+oslo.service.service = oslo_service.service:list_opts
+oslo.service.sslutils = oslo_service.sslutils:list_opts
+oslo.service.wsgi = oslo_service.wsgi:list_opts
diff -pruN 4.1.1-2/oslo.service.egg-info/not-zip-safe 4.2.2-0ubuntu1/oslo.service.egg-info/not-zip-safe
--- 4.1.1-2/oslo.service.egg-info/not-zip-safe	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo.service.egg-info/not-zip-safe	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1 @@
+
diff -pruN 4.1.1-2/oslo.service.egg-info/pbr.json 4.2.2-0ubuntu1/oslo.service.egg-info/pbr.json
--- 4.1.1-2/oslo.service.egg-info/pbr.json	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo.service.egg-info/pbr.json	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1 @@
+{"git_version": "501b9e8", "is_release": true}
\ No newline at end of file
diff -pruN 4.1.1-2/oslo.service.egg-info/requires.txt 4.2.2-0ubuntu1/oslo.service.egg-info/requires.txt
--- 4.1.1-2/oslo.service.egg-info/requires.txt	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo.service.egg-info/requires.txt	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1,17 @@
+WebOb>=1.7.1
+debtcollector>=1.2.0
+eventlet>=0.27.0
+greenlet>=0.4.15
+oslo.utils>=3.40.2
+oslo.concurrency>=3.25.0
+oslo.config>=5.1.0
+oslo.log>=3.36.0
+oslo.i18n>=3.15.3
+PasteDeploy>=1.5.0
+Routes>=2.3.1
+Paste>=2.0.2
+Yappi>=1.0
+
+[threading]
+cotyledon>=2.0.0
+futurist>=3.1.1
diff -pruN 4.1.1-2/oslo.service.egg-info/top_level.txt 4.2.2-0ubuntu1/oslo.service.egg-info/top_level.txt
--- 4.1.1-2/oslo.service.egg-info/top_level.txt	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo.service.egg-info/top_level.txt	2025-07-10 09:43:35.000000000 +0000
@@ -0,0 +1 @@
+oslo_service
diff -pruN 4.1.1-2/oslo_service/backend/__init__.py 4.2.2-0ubuntu1/oslo_service/backend/__init__.py
--- 4.1.1-2/oslo_service/backend/__init__.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/__init__.py	2025-07-10 09:42:42.000000000 +0000
@@ -12,12 +12,14 @@
 # implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+
 from __future__ import annotations
 
 import enum
 import importlib
 import logging
 from typing import Any
+from typing import Callable
 from typing import TYPE_CHECKING
 
 from . import exceptions
@@ -35,31 +37,61 @@ class BackendType(enum.Enum):
 
 DEFAULT_BACKEND_TYPE = BackendType.EVENTLET
 
-_cached_backend_type: BackendType | None = None  # backend type
-_cached_backend: BaseBackend | None = None  # current backend
-_cached_components: dict[str, Any] | None = None  # backend components
+_cached_backend_type: BackendType | None = None
+_cached_backend: BaseBackend | None = None
+_cached_components: dict[str, Any] | None = None
+
+# optional override hook
+_backend_hook: Callable[[], BackendType] | None = None
+
+
+def register_backend_default_hook(hook: Callable[[], BackendType]) -> None:
+    """Register a hook that decides the default backend type.
+
+    This is used when no init_backend() call has been made, and
+    get_backend() is called. The hook will be invoked to determine
+    the backend type to use *only if* no backend has been selected yet.
+
+    Example:
+
+        def my_hook():
+            return BackendType.THREADING
+
+        register_backend_default_hook(my_hook)
+
+    :param hook: A callable that returns a BackendType
+    """
+    global _backend_hook
+    _backend_hook = hook
 
 
 def _reset_backend() -> None:
-    """used by test functions to reset the selected backend"""
+    """Used by test functions to reset the selected backend."""
+
+    global _cached_backend, _cached_components
+    global _cached_backend_type, _backend_hook
 
-    global _cached_backend, _cached_components, _cached_backend_type
     _cached_backend_type = _cached_backend = _cached_components = None
+    _backend_hook = None
 
 
 def init_backend(type_: BackendType) -> None:
-    """establish which backend will be used when get_backend() is called"""
+    """Establish which backend will be used when get_backend() is called."""
 
     global _cached_backend, _cached_components, _cached_backend_type
 
     if _cached_backend_type is not None:
-        raise exceptions.BackendAlreadySelected(
-            f"The {_cached_backend_type.value!r} backend is already set up"
-        )
+        if _cached_backend_type != type_:
+            raise exceptions.BackendAlreadySelected(
+                f"Backend already set to {_cached_backend_type.value!r},"
+                f" cannot reinitialize with {type_.value!r}"
+            )
 
-    backend_name = type_.value
+        return  # already initialized with same value; no-op
 
+    backend_name = type_.value
     LOG.info(f"Loading backend: {backend_name}")
+
     try:
         module_name = f"oslo_service.backend.{backend_name}"
         module = importlib.import_module(module_name)
@@ -81,17 +113,33 @@ def init_backend(type_: BackendType) ->
 
 
 def get_backend() -> BaseBackend:
-    """Load backend dynamically based on the default constant."""
+    """Load backend dynamically based on the default constant or hook."""
 
     global _cached_backend
     if _cached_backend is None:
-        init_backend(DEFAULT_BACKEND_TYPE)
+        type_ = DEFAULT_BACKEND_TYPE
+
+        if _backend_hook is not None:
+            try:
+                type_ = _backend_hook()
+                LOG.info(f"Backend hook selected: {type_.value}")
+            except Exception:
+                LOG.exception(
+                    "Backend hook raised an exception."
+                    " Falling back to default.")
+
+        init_backend(type_)
 
     assert _cached_backend is not None  # nosec B101 : this is for typing
 
     return _cached_backend
 
 
+def get_backend_type() -> BackendType | None:
+    """Return the type of the current backend, or None if not set."""
+    return _cached_backend_type
+
+
 def get_component(name: str) -> Any:
     """Retrieve a specific component from the backend."""
     global _cached_components
@@ -103,4 +151,5 @@ def get_component(name: str) -> Any:
 
     if name not in _cached_components:
         raise KeyError(f"Component {name!r} not found in backend.")
+
     return _cached_components[name]
diff -pruN 4.1.1-2/oslo_service/backend/base.py 4.2.2-0ubuntu1/oslo_service/backend/base.py
--- 4.1.1-2/oslo_service/backend/base.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/base.py	2025-07-10 09:42:42.000000000 +0000
@@ -15,17 +15,16 @@
 
 from __future__ import annotations
 
-from abc import ABC
-from abc import abstractmethod
+import abc
 from typing import Any
 
 from oslo_service.backend.exceptions import BackendComponentNotAvailable
 
 
-class BaseBackend(ABC):
+class BaseBackend(abc.ABC):
     """Base class for all backend implementations."""
 
-    @abstractmethod
+    @abc.abstractmethod
     def get_service_components(self) -> dict[str, Any]:
         """Return the backend components.
 
@@ -42,6 +41,29 @@ class BaseBackend(ABC):
         pass
 
 
+class ServiceBase(metaclass=abc.ABCMeta):
+    """Base class for all services."""
+
+    @abc.abstractmethod
+    def start(self):
+        """Start service."""
+
+    @abc.abstractmethod
+    def stop(self):
+        """Stop service."""
+
+    @abc.abstractmethod
+    def wait(self):
+        """Wait for service to complete."""
+
+    @abc.abstractmethod
+    def reset(self):
+        """Reset service.
+
+        Called in case a service running in daemon mode receives SIGHUP.
+        """
+
+
 class ComponentRegistry:
     """A registry to manage access to backend components.
 
@@ -49,12 +71,13 @@ class ComponentRegistry:
     raises an explicit error, improving clarity and debugging. It acts
     as a centralized registry for backend components.
     """
+
     def __init__(self, components):
         """Initialize the registry with a dictionary of components.
 
-        :param components: A dictionary containing backend components, where
-                           the keys are component names and the values are
-                           the respective implementations.
+        :param components: A dictionary containing backend components,
+            where the keys are component names and the values are the
+            respective implementations.
         """
         self._components = components
 
@@ -62,8 +85,8 @@ class ComponentRegistry:
         """Retrieve a component by its key from the registry.
 
         :param key: The name of the component to retrieve.
-        :raises NotImplementedError: If the component is
-                                     not registered or available.
+        :raises NotImplementedError: If the component is not registered
+            or available.
         :return: The requested component instance.
         """
         if key not in self._components or self._components[key] is None:
@@ -75,7 +98,7 @@ class ComponentRegistry:
         """Check if a component is registered and available.
 
         :param key: The name of the component to check.
-        :return: True if the component is registered
-                 and available, False otherwise.
+        :return: True if the component is registered and available,
+            False otherwise.
         """
         return key in self._components and self._components[key] is not None
diff -pruN 4.1.1-2/oslo_service/backend/common/constants.py 4.2.2-0ubuntu1/oslo_service/backend/common/constants.py
--- 4.1.1-2/oslo_service/backend/common/constants.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/common/constants.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,16 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Restart method options
+_LAUNCHER_RESTART_METHODS = ['reload', 'mutate']
diff -pruN 4.1.1-2/oslo_service/backend/common/daemon_utils.py 4.2.2-0ubuntu1/oslo_service/backend/common/daemon_utils.py
--- 4.1.1-2/oslo_service/backend/common/daemon_utils.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/common/daemon_utils.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,33 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import errno
+import io
+import os
+import signal
+import sys
+
+
+def is_daemon():
+    try:
+        return os.getpgrp() != os.tcgetpgrp(sys.stdout.fileno())
+    except io.UnsupportedOperation:
+        return True
+    except OSError as err:
+        return err.errno == errno.ENOTTY or False
+
+
+def is_sighup_and_daemon(signo, signal_handler):
+    return (signal_handler.is_signal_supported('SIGHUP') and
+            signo == signal.SIGHUP and is_daemon())
diff -pruN 4.1.1-2/oslo_service/backend/common/signal_utils.py 4.2.2-0ubuntu1/oslo_service/backend/common/signal_utils.py
--- 4.1.1-2/oslo_service/backend/common/signal_utils.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/common/signal_utils.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,38 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import signal
+
+
+def get_signal_mappings(ignore=('SIG_DFL', 'SIG_IGN')):
+    signals_by_name = {
+        name: getattr(signal, name)
+        for name in dir(signal)
+        if name.startswith('SIG') and name not in ignore
+    }
+    signals_to_name = {v: k for k, v in signals_by_name.items()}
+
+    return signals_by_name, signals_to_name
+
+
+class SignalExit(SystemExit):
+    """Raised to indicate a signal-based exit.
+
+    This exception is commonly raised when the process receives a termination
+    signal (e.g., SIGTERM, SIGINT). The signal number is stored in `signo`.
+    """
+
+    def __init__(self, signo, exccode=1):
+        super().__init__(exccode)
+        self.signo = signo
diff -pruN 4.1.1-2/oslo_service/backend/common/singleton.py 4.2.2-0ubuntu1/oslo_service/backend/common/singleton.py
--- 4.1.1-2/oslo_service/backend/common/singleton.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/common/singleton.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,26 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from oslo_concurrency import lockutils
+
+
+class Singleton(type):
+    _instances = {}
+    _semaphores = lockutils.Semaphores()
+
+    def __call__(cls, *args, **kwargs):
+        with lockutils.lock('singleton_lock', semaphores=cls._semaphores):
+            if cls not in cls._instances:
+                cls._instances[cls] = super().__call__(*args, **kwargs)
+        return cls._instances[cls]
diff -pruN 4.1.1-2/oslo_service/backend/common/validation_utils.py 4.2.2-0ubuntu1/oslo_service/backend/common/validation_utils.py
--- 4.1.1-2/oslo_service/backend/common/validation_utils.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/common/validation_utils.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,23 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from oslo_service._i18n import _
+from oslo_service.backend.base import ServiceBase
+
+
+def check_service_base(service):
+    if not isinstance(service, ServiceBase):
+        raise TypeError(
+            _("Service %(service)s must be an instance of %(base)s!")
+            % {'service': service, 'base': ServiceBase})
diff -pruN 4.1.1-2/oslo_service/backend/eventlet/__init__.py 4.2.2-0ubuntu1/oslo_service/backend/eventlet/__init__.py
--- 4.1.1-2/oslo_service/backend/eventlet/__init__.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/eventlet/__init__.py	2025-07-10 09:42:42.000000000 +0000
@@ -15,6 +15,9 @@
 
 
 from oslo_service.backend.base import BaseBackend
+from oslo_service.backend.common import daemon_utils
+from oslo_service.backend.common import signal_utils
+from oslo_service.backend.common import singleton
 from oslo_service.backend.eventlet import loopingcall
 from oslo_service.backend.eventlet import service
 from oslo_service.backend.eventlet import threadgroup
@@ -37,8 +40,8 @@ class EventletBackend(BaseBackend):
             "Services": service.Services,
             "ServiceWrapper": service.ServiceWrapper,
             "SignalHandler": service.SignalHandler,
-            "SignalExit": service.SignalExit,
-            "Singleton": service.Singleton,
+            "SignalExit": signal_utils.SignalExit,
+            "Singleton": singleton.Singleton,
 
             # Looping call-related classes
             "LoopingCallBase": loopingcall.LoopingCallBase,
@@ -57,6 +60,6 @@ class EventletBackend(BaseBackend):
 
             # Functions
             "launch": service.launch,
-            "_is_daemon": service._is_daemon,
-            "_is_sighup_and_daemon": service._is_sighup_and_daemon,
+            "_is_daemon": daemon_utils.is_daemon,
+            "_is_sighup_and_daemon": daemon_utils.is_sighup_and_daemon,
         }
diff -pruN 4.1.1-2/oslo_service/backend/eventlet/loopingcall.py 4.2.2-0ubuntu1/oslo_service/backend/eventlet/loopingcall.py
--- 4.1.1-2/oslo_service/backend/eventlet/loopingcall.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/eventlet/loopingcall.py	2025-07-10 09:42:42.000000000 +0000
@@ -37,13 +37,13 @@ LOG = logging.getLogger(__name__)
 class LoopingCallDone(Exception):
     """Exception to break out and stop a LoopingCallBase.
 
-    The poll-function passed to LoopingCallBase can raise this exception to
-    break out of the loop normally. This is somewhat analogous to
+    The poll-function passed to LoopingCallBase can raise this exception
+    to break out of the loop normally. This is somewhat analogous to
     StopIteration.
 
-    An optional return-value can be included as the argument to the exception;
-    this return-value will be returned by LoopingCallBase.wait()
-
+    An optional return-value can be included as the argument to the
+    exception; this return-value will be returned by
+    LoopingCallBase.wait()
     """
 
     def __init__(self, retvalue=True):
@@ -109,16 +109,17 @@ class LoopingCallBase:
         self._abort.wait(timeout)
 
     def _start(self, idle_for, initial_delay=None, stop_on_exception=True):
-        """Start the looping
+        """Start the looping.
 
-        :param idle_for: Callable that takes two positional arguments, returns
-                         how long to idle for. The first positional argument is
-                         the last result from the function being looped and the
-                         second positional argument is the time it took to
-                         calculate that result.
-        :param initial_delay: How long to delay before starting the looping.
-                              Value is in seconds.
-        :param stop_on_exception: Whether to stop if an exception occurs.
+        :param idle_for: Callable that takes two positional arguments,
+            returns how long to idle for. The first positional argument
+            is the last result from the function being looped and the
+            second positional argument is the time it took to calculate
+            that result.
+        :param initial_delay: How long to delay before starting the
+            looping. Value is in seconds.
+        :param stop_on_exception: Whether to stop if an exception
+            occurs.
         :returns: eventlet event instance
         """
         if self._thread is not None:
@@ -359,33 +360,33 @@ class BackOffLoopingCall(LoopingCallBase
 class RetryDecorator:
     """Decorator for retrying a function upon suggested exceptions.
 
-    The decorated function is retried for the given number of times, and the
-    sleep time between the retries is incremented until max sleep time is
-    reached. If the max retry count is set to -1, then the decorated function
-    is invoked indefinitely until an exception is thrown, and the caught
-    exception is not in the list of suggested exceptions.
+    The decorated function is retried for the given number of times, and
+    the sleep time between the retries is incremented until max sleep
+    time is reached. If the max retry count is set to -1, then the
+    decorated function is invoked indefinitely until an exception is
+    thrown, and the caught exception is not in the list of suggested
+    exceptions.
     """
 
     def __init__(self, max_retry_count=-1, inc_sleep_time=10,
                  max_sleep_time=60, exceptions=()):
         """Configure the retry object using the input params.
 
-        :param max_retry_count: maximum number of times the given function must
-                                be retried when one of the input 'exceptions'
-                                is caught. When set to -1, it will be retried
-                                indefinitely until an exception is thrown
-                                and the caught exception is not in param
-                                exceptions.
-        :param inc_sleep_time: incremental time in seconds for sleep time
-                               between retries
-        :param max_sleep_time: max sleep time in seconds beyond which the sleep
-                               time will not be incremented using param
-                               inc_sleep_time. On reaching this threshold,
-                               max_sleep_time will be used as the sleep time.
-        :param exceptions: suggested exceptions for which the function must be
-                           retried, if no exceptions are provided (the default)
-                           then all exceptions will be reraised, and no
-                           retrying will be triggered.
+        :param max_retry_count: maximum number of times the given
+            function must be retried when one of the input 'exceptions'
+            is caught. When set to -1, it will be retried indefinitely
+            until an exception is thrown and the caught exception is not
+            in param exceptions.
+        :param inc_sleep_time: incremental time in seconds for sleep
+            time between retries
+        :param max_sleep_time: max sleep time in seconds beyond which
+            the sleep time will not be incremented using param
+            inc_sleep_time. On reaching this threshold, max_sleep_time
+            will be used as the sleep time.
+        :param exceptions: suggested exceptions for which the function
+            must be retried, if no exceptions are provided (the default)
+            then all exceptions will be reraised, and no retrying will
+            be triggered.
         """
         self._max_retry_count = max_retry_count
         self._inc_sleep_time = inc_sleep_time
diff -pruN 4.1.1-2/oslo_service/backend/eventlet/service.py 4.2.2-0ubuntu1/oslo_service/backend/eventlet/service.py
--- 4.1.1-2/oslo_service/backend/eventlet/service.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/eventlet/service.py	2025-07-10 09:42:42.000000000 +0000
@@ -14,17 +14,13 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
-
-
 """Generic Node base class for all workers that run on hosts."""
 
-import abc
 import collections
 import errno
 import functools
 import gc
 import inspect
-import io
 import logging
 import os
 import random
@@ -36,9 +32,21 @@ import eventlet
 from eventlet import event
 from eventlet import tpool
 
-from oslo_concurrency import lockutils
 from oslo_service._i18n import _
 from oslo_service import _options
+from oslo_service.backend.base import ServiceBase
+from oslo_service.backend.common.constants import \
+    _LAUNCHER_RESTART_METHODS
+from oslo_service.backend.common.daemon_utils import \
+    is_sighup_and_daemon as _is_sighup_and_daemon
+from oslo_service.backend.common.signal_utils import \
+    get_signal_mappings
+from oslo_service.backend.common.signal_utils import \
+    SignalExit
+from oslo_service.backend.common.singleton import \
+    Singleton
+from oslo_service.backend.common.validation_utils import \
+    check_service_base as _check_service_base
 from oslo_service.backend.eventlet import threadgroup
 from oslo_service import eventlet_backdoor
 from oslo_service import systemd
@@ -46,78 +54,6 @@ from oslo_service import systemd
 
 LOG = logging.getLogger(__name__)
 
-_LAUNCHER_RESTART_METHODS = ['reload', 'mutate']
-
-
-def _is_daemon():
-    # The process group for a foreground process will match the
-    # process group of the controlling terminal. If those values do
-    # not match, or ioctl() fails on the stdout file handle, we assume
-    # the process is running in the background as a daemon.
-    # http://www.gnu.org/software/bash/manual/bashref.html#Job-Control-Basics
-    try:
-        is_daemon = os.getpgrp() != os.tcgetpgrp(sys.stdout.fileno())
-    except io.UnsupportedOperation:
-        # Could not get the fileno for stdout, so we must be a daemon.
-        is_daemon = True
-    except OSError as err:
-        if err.errno == errno.ENOTTY:
-            # Assume we are a daemon because there is no terminal.
-            is_daemon = True
-        else:
-            raise
-    return is_daemon
-
-
-def _is_sighup_and_daemon(signo):
-    if not (SignalHandler().is_signal_supported('SIGHUP') and
-            signo == signal.SIGHUP):
-        # Avoid checking if we are a daemon, because the signal isn't
-        # SIGHUP.
-        return False
-    return _is_daemon()
-
-
-def _check_service_base(service):
-    if not isinstance(service, ServiceBase):
-        raise TypeError(_("Service %(service)s must an instance of %(base)s!")
-                        % {'service': service, 'base': ServiceBase})
-
-
-class ServiceBase(metaclass=abc.ABCMeta):
-    """Base class for all services."""
-
-    @abc.abstractmethod
-    def start(self):
-        """Start service."""
-
-    @abc.abstractmethod
-    def stop(self):
-        """Stop service."""
-
-    @abc.abstractmethod
-    def wait(self):
-        """Wait for service to complete."""
-
-    @abc.abstractmethod
-    def reset(self):
-        """Reset service.
-
-        Called in case service running in daemon mode receives SIGHUP.
-        """
-
-
-class Singleton(type):
-    _instances = {}
-    _semaphores = lockutils.Semaphores()
-
-    def __call__(cls, *args, **kwargs):
-        with lockutils.lock('singleton_lock', semaphores=cls._semaphores):
-            if cls not in cls._instances:
-                cls._instances[cls] = super().__call__(
-                    *args, **kwargs)
-        return cls._instances[cls]
-
 
 class SignalHandler(metaclass=Singleton):
 
@@ -128,14 +64,7 @@ class SignalHandler(metaclass=Singleton)
 
         # Map all signal names to signal integer values and create a
         # reverse mapping (for easier + quick lookup).
-        self._ignore_signals = ('SIG_DFL', 'SIG_IGN')
-        self._signals_by_name = {name: getattr(signal, name)
-                                 for name in dir(signal)
-                                 if name.startswith("SIG") and
-                                 name not in self._ignore_signals}
-        self.signals_to_name = {
-            sigval: name
-            for (name, sigval) in self._signals_by_name.items()}
+        self._signals_by_name, self.signals_to_name = get_signal_mappings()
         self._signal_handlers = collections.defaultdict(list)
         self.clear()
 
@@ -192,8 +121,8 @@ class SignalHandler(metaclass=Singleton)
     def __setup_signal_interruption(self):
         """Set up to do the Right Thing with signals during poll() and sleep().
 
-        Deal with the changes introduced in PEP 475 that prevent a signal from
-        interrupting eventlet's call to poll() or sleep().
+        Deal with the changes introduced in PEP 475 that prevent a
+        signal from interrupting eventlet's call to poll() or sleep().
         """
         select_module = eventlet.patcher.original('select')
         self.__force_interrupt_on_signal = hasattr(select_module, 'poll')
@@ -245,10 +174,9 @@ class Launcher:
         """Initialize the service launcher.
 
         :param restart_method: If 'reload', calls reload_config_files on
-            SIGHUP. If 'mutate', calls mutate_config_files on SIGHUP. Other
-            values produce a ValueError.
+            SIGHUP. If 'mutate', calls mutate_config_files on SIGHUP.
+            Other values produce a ValueError.
         :returns: None
-
         """
         self.conf = conf
         conf.register_opts(_options.service_opts)
@@ -266,7 +194,6 @@ class Launcher:
                         ProcessLauncher.launch_service. It must be None, 1 or
                         omitted.
         :returns: None
-
         """
         if workers is not None and workers != 1:
             raise ValueError(_("Launcher asked to start multiple workers"))
@@ -278,7 +205,6 @@ class Launcher:
         """Stop all services which are currently running.
 
         :returns: None
-
         """
         self.services.stop()
 
@@ -286,7 +212,6 @@ class Launcher:
         """Wait until all services have been stopped, and then return.
 
         :returns: None
-
         """
         self.services.wait()
 
@@ -294,7 +219,7 @@ class Launcher:
         """Reload config files and restart service.
 
         :returns: The return value from reload_config_files or
-          mutate_config_files, according to the restart_method.
+            mutate_config_files, according to the restart_method.
         """
         if self.restart_method == 'reload':
             self.conf.reload_config_files()
@@ -303,14 +228,9 @@ class Launcher:
         self.services.restart()
 
 
-class SignalExit(SystemExit):
-    def __init__(self, signo, exccode=1):
-        super().__init__(exccode)
-        self.signo = signo
-
-
 class ServiceLauncher(Launcher):
     """Runs one or more service in a parent process."""
+
     def __init__(self, conf, restart_method='reload'):
         """Constructor.
 
@@ -381,7 +301,7 @@ class ServiceLauncher(Launcher):
         while True:
             self.handle_signal()
             status, signo = self._wait_for_exit_or_signal()
-            if not _is_sighup_and_daemon(signo):
+            if not _is_sighup_and_daemon(signo, SignalHandler()):
                 break
             self.restart()
 
@@ -405,10 +325,10 @@ class ProcessLauncher:
 
         :param conf: an instance of ConfigOpts
         :param wait_interval: The interval to sleep for between checks
-                              of child process exit.
+            of child process exit.
         :param restart_method: If 'reload', calls reload_config_files on
-            SIGHUP. If 'mutate', calls mutate_config_files on SIGHUP. Other
-            values produce a ValueError.
+            SIGHUP. If 'mutate', calls mutate_config_files on SIGHUP.
+            Other values produce a ValueError.
         """
         self.conf = conf
         conf.register_opts(_options.service_opts)
@@ -563,7 +483,7 @@ class ProcessLauncher:
                 self._child_process_handle_signal()
                 status, signo = self._child_wait_for_exit_or_signal(
                     self.launcher)
-                if not _is_sighup_and_daemon(signo):
+                if not _is_sighup_and_daemon(signo, SignalHandler()):
                     self.launcher.wait()
                     break
                 self.launcher.restart()
@@ -580,10 +500,10 @@ class ProcessLauncher:
     def launch_service(self, service, workers=1):
         """Launch a service with a given number of workers.
 
-       :param service: a service to launch, must be an instance of
-              :class:`oslo_service.service.ServiceBase`
-       :param workers: a number of processes in which a service
-              will be running
+        :param service: a service to launch, must be an instance of
+               :class:`oslo_service.service.ServiceBase`
+        :param workers: a number of processes in which a service
+               will be running
         """
         _check_service_base(service)
         wrap = ServiceWrapper(service, workers)
@@ -657,7 +577,7 @@ class ProcessLauncher:
 
                 signame = self.signal_handler.signals_to_name[self.sigcaught]
                 LOG.info('Caught %s, stopping children', signame)
-                if not _is_sighup_and_daemon(self.sigcaught):
+                if not _is_sighup_and_daemon(self.sigcaught, SignalHandler()):
                     break
 
                 child_signal = signal.SIGTERM
@@ -725,8 +645,8 @@ class Service(ServiceBase):
     def stop(self, graceful=False):
         """Stop a service.
 
-        :param graceful: indicates whether to wait for all threads to finish
-               or terminate them instantly
+        :param graceful: indicates whether to wait for all threads to
+            finish or terminate them instantly
         """
         self.tg.stop(graceful)
 
@@ -798,7 +718,6 @@ class Services:
         :param service: service to run
         :param done: event to wait on until a shutdown is triggered
         :returns: None
-
         """
         try:
             service.start()
diff -pruN 4.1.1-2/oslo_service/backend/eventlet/threadgroup.py 4.2.2-0ubuntu1/oslo_service/backend/eventlet/threadgroup.py
--- 4.1.1-2/oslo_service/backend/eventlet/threadgroup.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/eventlet/threadgroup.py	2025-07-10 09:42:42.000000000 +0000
@@ -39,10 +39,11 @@ def _on_thread_done(_greenthread, group,
 class Thread:
     """Wrapper around a greenthread.
 
-     Holds a reference to the :class:`ThreadGroup`. The Thread will notify
-     the :class:`ThreadGroup` when it has done so it can be removed from
-     the threads list.
+    Holds a reference to the :class:`ThreadGroup`. The Thread will notify
+    the :class:`ThreadGroup` when it has done so it can be removed from
+    the threads list.
     """
+
     def __init__(self, thread, group, link=True):
         self.thread = thread
         if link:
@@ -91,8 +92,8 @@ class ThreadGroup:
     def __init__(self, thread_pool_size=10):
         """Create a ThreadGroup with a pool of greenthreads.
 
-        :param thread_pool_size: the maximum number of threads allowed to run
-                                 concurrently.
+        :param thread_pool_size: the maximum number of threads allowed
+            to run concurrently.
         """
         self.pool = greenpool.GreenPool(thread_pool_size)
         self.threads = []
@@ -244,8 +245,8 @@ class ThreadGroup:
     def thread_done(self, thread):
         """Remove a completed thread from the group.
 
-        This method is automatically called on completion of a thread in the
-        group, and should not be called explicitly.
+        This method is automatically called on completion of a thread in
+        the group, and should not be called explicitly.
         """
         self.threads.remove(thread)
 
diff -pruN 4.1.1-2/oslo_service/backend/exceptions.py 4.2.2-0ubuntu1/oslo_service/backend/exceptions.py
--- 4.1.1-2/oslo_service/backend/exceptions.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/exceptions.py	2025-07-10 09:42:42.000000000 +0000
@@ -15,7 +15,7 @@
 
 
 class BackendAlreadySelected(Exception):
-    """raised when init_backend() is called more than once"""
+    """Raised when init_backend() is called more than once."""
     pass
 
 
diff -pruN 4.1.1-2/oslo_service/backend/threading/__init__.py 4.2.2-0ubuntu1/oslo_service/backend/threading/__init__.py
--- 4.1.1-2/oslo_service/backend/threading/__init__.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/threading/__init__.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,60 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from oslo_service.backend.base import BaseBackend
+from oslo_service.backend.common import daemon_utils
+from oslo_service.backend.common import signal_utils
+from oslo_service.backend.common import singleton
+from oslo_service.backend.threading import loopingcall
+from oslo_service.backend.threading import service
+from oslo_service.backend.threading import threadgroup
+
+
+class ThreadingBackend(BaseBackend):
+    """Backend implementation using Python threading and Cotyledon."""
+
+    @staticmethod
+    def get_service_components():
+        """Return the components provided by the Threading backend."""
+
+        return {
+            # Service-related classes
+            "ServiceBase": service.ServiceBase,
+            "ServiceLauncher": service.ProcessLauncher,
+            "Launcher": service.ProcessLauncher,
+            "ProcessLauncher": service.ProcessLauncher,
+            "Service": service.Service,
+            "Services": service.Services,
+            "ServiceWrapper": service.ServiceWrapper,
+            "SignalExit": signal_utils.SignalExit,
+            "SignalHandler": service.SignalHandler,
+            "Singleton": singleton.Singleton,
+            # Looping call-related classes
+            "LoopingCallBase": loopingcall.LoopingCallBase,
+            "LoopingCallDone": loopingcall.LoopingCallDone,
+            "LoopingCallTimeOut": loopingcall.LoopingCallTimeOut,
+            "FixedIntervalLoopingCall": loopingcall.FixedIntervalLoopingCall,
+            "FixedIntervalWithTimeoutLoopingCall":
+                loopingcall.FixedIntervalWithTimeoutLoopingCall,
+            "DynamicLoopingCall": loopingcall.DynamicLoopingCall,
+            "BackOffLoopingCall": loopingcall.BackOffLoopingCall,
+            "RetryDecorator": loopingcall.RetryDecorator,
+            # Thread group-related classes
+            "ThreadGroup": threadgroup.ThreadGroup,
+            "Thread": threadgroup.Thread,
+            # Functions
+            "_is_daemon": daemon_utils.is_daemon,
+            "_is_sighup_and_daemon": daemon_utils.is_sighup_and_daemon,
+            "launch": service.launch,
+        }
diff -pruN 4.1.1-2/oslo_service/backend/threading/loopingcall.py 4.2.2-0ubuntu1/oslo_service/backend/threading/loopingcall.py
--- 4.1.1-2/oslo_service/backend/threading/loopingcall.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/threading/loopingcall.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,403 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import functools
+import random
+import sys
+import threading
+import time
+
+import futurist
+from oslo_log import log as logging
+from oslo_utils import excutils
+from oslo_utils import reflection
+from oslo_utils import timeutils
+
+from oslo_service._i18n import _
+
+LOG = logging.getLogger(__name__)
+
+
+class LoopingCallDone(Exception):
+    """Exception to break out and stop a LoopingCallBase.
+
+    The function passed to a looping call may raise this exception to
+    break out of the loop normally. An optional return value may be
+    provided; this value will be returned by LoopingCallBase.wait().
+    """
+
+    def __init__(self, retvalue=True):
+        """:param retvalue: Value that LoopingCallBase.wait() should return."""
+        self.retvalue = retvalue
+
+
+class LoopingCallTimeOut(Exception):
+    """Exception raised when a LoopingCall times out."""
+    pass
+
+
+class FutureEvent:
+    """A simple event object that can carry a result or an exception."""
+
+    def __init__(self):
+        self._event = threading.Event()
+        self._result = None
+        self._exc_info = None
+
+    def send(self, result):
+        self._result = result
+        self._event.set()
+
+    def send_exception(self, exc_type, exc_value, tb):
+        self._exc_info = (exc_type, exc_value, tb)
+        self._event.set()
+
+    def wait(self, timeout=None):
+        flag = self._event.wait(timeout)
+
+        if not flag:
+            raise RuntimeError("Timed out waiting for event")
+
+        if self._exc_info:
+            exc_type, exc_value, tb = self._exc_info
+            raise exc_value.with_traceback(tb)
+        return self._result
+
+
+def _safe_wrapper(f, kind, func_name):
+    """Wrapper that calls the wrapped function and logs errors as needed."""
+
+    def func(*args, **kwargs):
+        try:
+            return f(*args, **kwargs)
+        except LoopingCallDone:
+            raise  # Let the outer handler process this
+        except Exception:
+            LOG.error('%(kind)s %(func_name)r failed',
+                      {'kind': kind, 'func_name': func_name},
+                      exc_info=True)
+            return 0
+
+    return func
+
+
+class LoopingCallBase:
+    _KIND = _("Unknown looping call")
+    _RUN_ONLY_ONE_MESSAGE = _(
+        "A looping call can only run one function at a time")
+
+    def __init__(self, f=None, *args, **kwargs):
+        self.args = args
+        self.kwargs = kwargs
+        self.f = f
+        self._future = None
+        self.done = None
+        self._abort = threading.Event()  # When set, the loop stops
+
+    @property
+    def _running(self):
+        return not self._abort.is_set()
+
+    def stop(self):
+        if self._running:
+            self._abort.set()
+
+    def wait(self):
+        """Wait for the looping call to complete and return its result."""
+        return self.done.wait()
+
+    def _on_done(self, future):
+        self._future = None
+
+    def _sleep(self, timeout):
+        # Instead of eventlet.sleep, we wait on the abort event for timeout
+        # seconds.
+        self._abort.wait(timeout)
+
+    def _start(self, idle_for, initial_delay=None, stop_on_exception=True):
+        """Start the looping call.
+
+        :param idle_for: Callable taking two arguments (last result,
+            elapsed time) and returning how long to idle.
+        :param initial_delay: Delay (in seconds) before starting the
+            loop.
+        :param stop_on_exception: Whether to stop on exception.
+        :returns: A FutureEvent instance.
+        """
+
+        if self._future is not None:
+            raise RuntimeError(self._RUN_ONLY_ONE_MESSAGE)
+
+        self.done = FutureEvent()
+        self._abort.clear()
+
+        def _run_loop():
+            kind = self._KIND
+            func_name = reflection.get_callable_name(self.f)
+            func = self.f if stop_on_exception else _safe_wrapper(self.f, kind,
+                                                                  func_name)
+            if initial_delay:
+                self._sleep(initial_delay)
+            try:
+                watch = timeutils.StopWatch()
+
+                while self._running:
+                    watch.restart()
+                    result = func(*self.args, **self.kwargs)
+                    watch.stop()
+
+                    if not self._running:
+                        break
+
+                    idle = idle_for(result, watch.elapsed())
+                    LOG.debug(
+                        '%(kind)s %(func_name)r sleeping for %(idle).02f'
+                        ' seconds',
+                        {'func_name': func_name, 'idle': idle, 'kind': kind})
+                    self._sleep(idle)
+            except LoopingCallDone as e:
+                self.done.send(e.retvalue)
+            except Exception:
+                exc_info = sys.exc_info()
+                try:
+                    LOG.error('%(kind)s %(func_name)r failed',
+                              {'kind': kind, 'func_name': func_name},
+                              exc_info=exc_info)
+                    self.done.send_exception(*exc_info)
+                finally:
+                    del exc_info
+                return
+            else:
+                self.done.send(True)
+
+        # Use futurist's ThreadPoolExecutor to run the loop in a background
+        # thread.
+        executor = futurist.ThreadPoolExecutor(max_workers=1)
+        self._future = executor.submit(_run_loop)
+        self._future.add_done_callback(self._on_done)
+        return self.done
+
+    # NOTE: _elapsed() is a thin wrapper for StopWatch.elapsed()
+    def _elapsed(self, watch):
+        return watch.elapsed()
+
+
+class FixedIntervalLoopingCall(LoopingCallBase):
+    """A fixed interval looping call."""
+    _RUN_ONLY_ONE_MESSAGE = _(
+        "A fixed interval looping call can only run one function at a time")
+    _KIND = _('Fixed interval looping call')
+
+    def start(self, interval, initial_delay=None, stop_on_exception=True):
+        def _idle_for(result, elapsed):
+            delay = round(elapsed - interval, 2)
+            if delay > 0:
+                func_name = reflection.get_callable_name(self.f)
+                LOG.warning(
+                    'Function %(func_name)r run outlasted interval by'
+                    ' %(delay).2f sec',
+                    {'func_name': func_name, 'delay': delay})
+            return -delay if delay < 0 else 0
+
+        return self._start(_idle_for, initial_delay=initial_delay,
+                           stop_on_exception=stop_on_exception)
+
+
+class FixedIntervalWithTimeoutLoopingCall(LoopingCallBase):
+    """A fixed interval looping call with timeout checking."""
+    _RUN_ONLY_ONE_MESSAGE = _(
+        "A fixed interval looping call with timeout checking"
+        " can only run one function at a time")
+    _KIND = _('Fixed interval looping call with timeout checking.')
+
+    def start(self, interval, initial_delay=None, stop_on_exception=True,
+              timeout=0):
+        start_time = time.time()
+
+        def _idle_for(result, elapsed):
+            delay = round(elapsed - interval, 2)
+            if delay > 0:
+                func_name = reflection.get_callable_name(self.f)
+                LOG.warning(
+                    'Function %(func_name)r run outlasted interval by'
+                    ' %(delay).2f sec',
+                    {'func_name': func_name, 'delay': delay})
+            elapsed_time = time.time() - start_time
+            if timeout > 0 and elapsed_time > timeout:
+                raise LoopingCallTimeOut(
+                    _('Looping call timed out after %.02f seconds')
+                    % elapsed_time)
+
+            return -delay if delay < 0 else 0
+
+        return self._start(_idle_for, initial_delay=initial_delay,
+                           stop_on_exception=stop_on_exception)
+
+
+class DynamicLoopingCall(LoopingCallBase):
+    """A looping call which sleeps until the next known event.
+
+    The function called should return how long to sleep before being
+    called again.
+    """
+
+    _RUN_ONLY_ONE_MESSAGE = _(
+        "A dynamic interval looping call can only run one function at a time")
+    _TASK_MISSING_SLEEP_VALUE_MESSAGE = _(
+        "A dynamic interval looping call should supply either an interval or"
+        " periodic_interval_max"
+    )
+    _KIND = _('Dynamic interval looping call')
+
+    def start(self, initial_delay=None, periodic_interval_max=None,
+              stop_on_exception=True):
+        def _idle_for(suggested_delay, elapsed):
+            delay = suggested_delay
+            if delay is None:
+                if periodic_interval_max is not None:
+                    delay = periodic_interval_max
+                else:
+                    raise RuntimeError(self._TASK_MISSING_SLEEP_VALUE_MESSAGE)
+            else:
+                if periodic_interval_max is not None:
+                    delay = min(delay, periodic_interval_max)
+            return delay
+
+        return self._start(_idle_for, initial_delay=initial_delay,
+                           stop_on_exception=stop_on_exception)
+
+
+class BackOffLoopingCall(LoopingCallBase):
+    """Run a method in a loop with backoff on error.
+
+    The provided function should return True (indicating success, which resets
+    the backoff interval), False (indicating an error, triggering a backoff),
+    or raise LoopingCallDone(retvalue=...) to quit the loop.
+    """
+
+    _RNG = random.SystemRandom()
+    _KIND = _('Dynamic backoff interval looping call')
+    _RUN_ONLY_ONE_MESSAGE = _(
+        "A dynamic backoff interval looping call can only run one function at"
+        " a time")
+
+    def __init__(self, f=None, *args, **kwargs):
+        super().__init__(f=f, *args, **kwargs)
+        self._error_time = 0
+        self._interval = 1
+
+    def start(self, initial_delay=None, starting_interval=1, timeout=300,
+              max_interval=300, jitter=0.75, min_interval=0.001):
+        if self._future is not None:
+            raise RuntimeError(self._RUN_ONLY_ONE_MESSAGE)
+        # Reset state.
+        self._error_time = 0
+        self._interval = starting_interval
+
+        def _idle_for(success, _elapsed):
+            random_jitter = abs(self._RNG.gauss(jitter, 0.1))
+            if success:
+                # Reset error state on success.
+                self._interval = starting_interval
+                self._error_time = 0
+                return self._interval * random_jitter
+            else:
+                # Back off on error with jitter.
+                idle = max(self._interval * 2 * random_jitter, min_interval)
+                idle = min(idle, max_interval)
+                self._interval = max(self._interval * 2 * jitter, min_interval)
+                if timeout > 0 and self._error_time + idle > timeout:
+                    raise LoopingCallTimeOut(
+                        _('Looping call timed out after %.02f seconds') % (
+                            self._error_time + idle))
+                self._error_time += idle
+                return idle
+
+        return self._start(_idle_for, initial_delay=initial_delay)
+
+
+class RetryDecorator:
+    """Decorator for retrying a function upon suggested exceptions.
+
+    The decorated function is retried for the given number of times,
+    with an incrementally increasing sleep time between retries. A max
+    sleep time may be set. If max_retry_count is -1, the function is
+    retried indefinitely until an exception is raised that is not in the
+    suggested exceptions.
+    """
+
+    def __init__(self, max_retry_count=-1, inc_sleep_time=10,
+                 max_sleep_time=60, exceptions=()):
+        """Document parameters for retry behavior.
+
+        :param max_retry_count: Maximum number of retries for exceptions in
+                                'exceptions'. -1 means retry indefinitely.
+        :param inc_sleep_time: Incremental sleep time (seconds) between
+                               retries.
+        :param max_sleep_time: Maximum sleep time (seconds).
+        :param exceptions: A tuple of exception types to catch for retries.
+        """
+        self._max_retry_count = max_retry_count
+        self._inc_sleep_time = inc_sleep_time
+        self._max_sleep_time = max_sleep_time
+        self._exceptions = exceptions
+        self._retry_count = 0
+        self._sleep_time = 0
+
+    def __call__(self, f):
+        func_name = reflection.get_callable_name(f)
+
+        def _func(*args, **kwargs):
+            try:
+                if self._retry_count:
+                    LOG.debug(
+                        "Invoking %(func_name)s; retry count is"
+                        " %(retry_count)d.",
+                        {'func_name': func_name,
+                         'retry_count': self._retry_count})
+                result = f(*args, **kwargs)
+            except self._exceptions:
+                with excutils.save_and_reraise_exception() as ctxt:
+                    LOG.debug(
+                        "Exception in %(func_name)s occurred which is in the"
+                        " retry list.",
+                        {'func_name': func_name})
+                    if (self._max_retry_count != -1 and
+                            self._retry_count >= self._max_retry_count):
+                        LOG.debug(
+                            "Cannot retry %(func_name)s because retry count"
+                            " (%(retry_count)d) reached max"
+                            " (%(max_retry_count)d).",
+                            {'retry_count': self._retry_count,
+                             'max_retry_count': self._max_retry_count,
+                             'func_name': func_name})
+                    else:
+                        ctxt.reraise = False
+                        self._retry_count += 1
+                        self._sleep_time += self._inc_sleep_time
+
+                        return self._sleep_time
+
+            raise LoopingCallDone(result)
+
+        @functools.wraps(f)
+        def func(*args, **kwargs):
+            loop = DynamicLoopingCall(_func, *args, **kwargs)
+            evt = loop.start(periodic_interval_max=self._max_sleep_time)
+            LOG.debug("Waiting for function %s to return.", func_name)
+
+            return evt.wait()
+
+        return func
diff -pruN 4.1.1-2/oslo_service/backend/threading/service.py 4.2.2-0ubuntu1/oslo_service/backend/threading/service.py
--- 4.1.1-2/oslo_service/backend/threading/service.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/threading/service.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,270 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import collections
+import logging
+import signal
+import sys
+import threading
+import traceback
+import warnings
+
+import cotyledon
+from cotyledon import oslo_config_glue
+
+from oslo_service._i18n import _
+from oslo_service.backend.base import ServiceBase
+from oslo_service.backend.common.constants import _LAUNCHER_RESTART_METHODS
+from oslo_service.backend.common.signal_utils import get_signal_mappings
+from oslo_service.backend.common.singleton import Singleton
+from oslo_service.backend.common.validation_utils import \
+    check_service_base as _check_service_base
+from oslo_service.backend.threading import threadgroup
+
+LOG = logging.getLogger(__name__)
+
+
+class SignalHandler(metaclass=Singleton):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self._signals_by_name, self.signals_to_name = get_signal_mappings()
+        self._signal_handlers = collections.defaultdict(list)
+        self.clear()
+
+    def clear(self):
+        for sig in list(self._signal_handlers.keys()):
+            signal.signal(sig, signal.SIG_DFL)
+        self._signal_handlers.clear()
+
+    def add_handlers(self, signals, handler):
+        for sig in signals:
+            self.add_handler(sig, handler)
+
+    def add_handler(self, sig, handler):
+        if not self.is_signal_supported(sig):
+            return
+        signo = self._signals_by_name[sig]
+        self._signal_handlers[signo].append(handler)
+        signal.signal(signo, self._handle_signal)
+
+    def _handle_signal(self, signo, frame):
+        threading.Thread(target=self._handle_signal_cb,
+                         args=(signo, frame)).start()
+
+    def _handle_signal_cb(self, signo, frame):
+        for handler in reversed(self._signal_handlers[signo]):
+            handler(signo, frame)
+
+    def is_signal_supported(self, sig_name):
+        return sig_name in self._signals_by_name
+
+
+class ServiceWrapper(cotyledon.Service):
+    def __init__(self, worker_id, service_instance, **kwargs):
+        super().__init__(worker_id)
+        if not isinstance(service_instance, ServiceBase):
+            raise TypeError("Service must be an instance of ServiceBase")
+        self.service_instance = service_instance
+
+    def run(self):
+        try:
+            self.service_instance.start()
+            self.service_instance.wait()
+        except Exception:
+            traceback.print_exc()
+            sys.exit(1)
+
+    def terminate(self):
+        self.service_instance.stop()
+
+
+class Launcher:
+    def __init__(self):
+        self._launcher = None
+
+    def launch_service(self, service, workers=None):
+        _check_service_base(service)
+        if workers not in (None, 1):
+            raise NotImplementedError("Multiple workers is not supported.")
+        self._launcher = service
+        service.start()
+        return service
+
+    def wait(self):
+        if self._launcher:
+            self._launcher.wait()
+
+    def stop(self):
+        if self._launcher:
+            self._launcher.stop()
+
+    def restart(self):
+        if self._launcher:
+            self._launcher.stop()
+            self._launcher.start()
+
+
+class ServiceLauncher:
+    def __init__(self, conf, restart_method='reload'):
+        self.conf = conf
+        self.restart_method = restart_method
+        self.backdoor_port = None
+        self._manager = cotyledon.ServiceManager()
+        oslo_config_glue.setup(self._manager, conf)
+
+    def launch_service(self, service_instance, workers=1):
+        _check_service_base(service_instance)
+        service_instance.backdoor_port = self.backdoor_port
+        if not isinstance(workers, int) or workers < 1:
+            raise ValueError("Number of workers must be >= 1")
+        self._manager.add(ServiceWrapper, workers, args=(service_instance,))
+
+    def stop(self):
+        self._manager.shutdown()
+
+    def wait(self):
+        try:
+            return self._manager.run()
+        except SystemExit as exc:
+            self.stop()
+            return exc.code
+        except BaseException:
+            self.stop()
+            LOG.exception("Unhandled exception")
+            return 2
+
+
+class Service(ServiceBase):
+    def __init__(self, threads=1000):
+        super().__init__()
+        self.tg = threadgroup.ThreadGroup(threads)
+        self.backdoor_port = None
+
+    def reset(self):
+        pass
+
+    def start(self):
+        pass
+
+    def stop(self, graceful=False):
+        self.tg.stop(graceful)
+
+    def wait(self):
+        self.tg.waitall()
+
+
+class Services:
+    def __init__(self, restart_method='reload'):
+        if restart_method not in _LAUNCHER_RESTART_METHODS:
+            raise ValueError(_("Invalid restart_method: %s") % restart_method)
+        self.restart_method = restart_method
+        self.services = []
+        self.tg = threadgroup.ThreadGroup()
+        self.done = threading.Event()
+
+    def add(self, service):
+        self.services.append(service)
+        self.tg.add_thread(self.run_service, service, self.done)
+
+    def stop(self):
+        for service in self.services:
+            service.stop()
+        if not self.done.is_set():
+            self.done.set()
+        self.tg.stop()
+
+    def wait(self):
+        for service in self.services:
+            service.wait()
+        self.tg.wait()
+
+    def restart(self):
+        if self.restart_method == 'reload':
+            self.stop()
+            self.done = threading.Event()
+
+        for restart_service in self.services:
+            restart_service.reset()
+            if self.restart_method == 'reload':
+                self.tg.add_thread(
+                    self.run_service, restart_service, self.done)
+
+    @staticmethod
+    def run_service(service, done):
+        try:
+            service.start()
+        except Exception:
+            LOG.exception('Error starting service thread.')
+            raise SystemExit(1)
+        else:
+            done.wait()
+
+
+class ProcessLauncher:
+    def __init__(
+            self, conf, wait_interval=None, restart_method='reload',
+            no_fork=False):
+        self.conf = conf
+        self.restart_method = restart_method
+        self.no_fork = no_fork
+        self._manager = None
+
+        if wait_interval is not None:
+            warnings.warn(
+                "'wait_interval' is deprecated and has no effect in the"
+                " 'threading' backend. It is accepted only for compatibility"
+                " reasons and will be removed.",
+                category=DeprecationWarning,
+            )
+
+    def launch_service(self, service, workers=1):
+        _check_service_base(service)
+
+        if self.no_fork:
+            LOG.warning("no_fork=True: running service in main process")
+            service.start()
+            service.wait()
+            return
+
+        if self._manager is None:
+            self._manager = cotyledon.ServiceManager()
+            oslo_config_glue.setup(self._manager, self.conf)
+
+        self._manager.add(ServiceWrapper, workers, args=(service,))
+
+    def wait(self):
+        if self.no_fork:
+            return 0
+        return self._manager.run()
+
+    def stop(self):
+        LOG.info("Stopping service")
+        if self._manager:
+            self._manager.shutdown()
+
+
+def launch(conf, service, workers=1, restart_method='reload', no_fork=False):
+    if workers is not None and not isinstance(workers, int):
+        raise TypeError("Type of workers should be int!")
+    if workers is not None and workers <= 0:
+        raise ValueError("Number of workers should be positive!")
+
+    if workers == 1 and not no_fork:
+        launcher = ServiceLauncher(conf, restart_method=restart_method)
+    else:
+        launcher = ProcessLauncher(
+            conf, restart_method=restart_method, no_fork=no_fork)
+
+    launcher.launch_service(service, workers=workers)
+    return launcher
diff -pruN 4.1.1-2/oslo_service/backend/threading/threadgroup.py 4.2.2-0ubuntu1/oslo_service/backend/threading/threadgroup.py
--- 4.1.1-2/oslo_service/backend/threading/threadgroup.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/backend/threading/threadgroup.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,235 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import logging
+import threading
+import time
+import warnings
+
+from oslo_service.backend.threading import loopingcall
+
+LOG = logging.getLogger(__name__)
+
+
+class Thread:
+    """A simple wrapper around native threads.
+
+        This class mimics the eventlet Thread interface (stop, wait, link,
+        cancel) for compatibility with oslo.service consumers. The methods
+        `stop`, `link`, and `cancel` are implemented as no-ops in the threading
+        backend since native Python threads do not support these operations
+        natively.
+    """
+
+    def __init__(self, thread, group=None, link=True):
+        self.thread = thread
+        self._ident = thread.ident
+        self.group = group
+        # Optionally, support for a link callback can be added here.
+
+    @property
+    def ident(self):
+        return self._ident
+
+    def stop(self):
+        # These methods are no-ops in the threading backend because native
+        # Python threads cannot be forcefully stopped or cancelled once
+        # started. They are kept here to preserve API compatibility with the
+        # eventlet backend, where these methods are implemented.
+        pass
+
+    def wait(self):
+        self.thread.join()
+
+    def link(self, func, *args, **kwargs):
+        # Optionally schedule a callback after thread completion.
+        pass
+
+    def cancel(self, *throw_args):
+        # Optionally implement cancellation if required.
+        pass
+
+
+class ThreadGroup:
+    """A group of threads and timers similar to eventlet's GreenPool."""
+
+    def __init__(self, max_threads=1000):
+        self.max_threads = max_threads
+        self.threads = []
+        self.timers = []
+        self._lock = threading.Lock()
+
+    def __getstate__(self):
+        # Exclude _lock from pickling.
+        state = self.__dict__.copy()
+        if '_lock' in state:
+            del state['_lock']
+        return state
+
+    def __setstate__(self, state):
+        self.__dict__.update(state)
+        # Recreate the lock after unpickling.
+        self._lock = threading.Lock()
+
+    def add_timer(self, delay, callback, *args, **kwargs):
+        if args or kwargs:
+            warnings.warn(
+                "Calling add_timer() with arguments is deprecated. Use "
+                "add_timer_args() instead.",
+                DeprecationWarning
+            )
+        new_args = list(args)
+        if new_args and new_args[0] == delay:
+            new_args = new_args[1:]
+        return self.add_timer_args(delay, callback, new_args, kwargs)
+
+    def add_timer_args(self, interval, callback, args=None, kwargs=None,
+                       initial_delay=None, stop_on_exception=True):
+        args = args or []
+        kwargs = kwargs or {}
+        pulse = loopingcall.FixedIntervalLoopingCall(
+            callback, *args, **kwargs)
+        pulse.start(interval=interval,
+                    initial_delay=initial_delay,
+                    stop_on_exception=stop_on_exception)
+        self._set_attr(pulse, '_running', True)
+        pulse.args = tuple(args)
+        pulse.kw = kwargs
+        with self._lock:
+            self.timers.append(pulse)
+        return pulse
+
+    def add_dynamic_timer(self, callback, initial_delay, periodic_interval_max,
+                          *args, **kwargs):
+        warnings.warn(
+            "Calling add_dynamic_timer() with arguments is deprecated. Use "
+            "add_dynamic_timer_args() instead.",
+            DeprecationWarning
+        )
+        return self.add_dynamic_timer_args(
+            callback, list(args), kwargs, initial_delay=initial_delay,
+            periodic_interval_max=periodic_interval_max)
+
+    def add_dynamic_timer_args(self, callback, args=None, kwargs=None,
+                               initial_delay=None, periodic_interval_max=None,
+                               stop_on_exception=True):
+        args = args or []
+        kwargs = kwargs or {}
+        timer = loopingcall.DynamicLoopingCall(callback, *args, **kwargs)
+        timer.start(initial_delay=initial_delay,
+                    periodic_interval_max=periodic_interval_max,
+                    stop_on_exception=stop_on_exception)
+        self._set_attr(timer, '_running', True)
+        timer.args = tuple(args)
+        timer.kw = kwargs
+        with self._lock:
+            self.timers.append(timer)
+        return timer
+
+    def add_thread(self, callback, *args, **kwargs):
+        with self._lock:
+            if len(self.threads) >= self.max_threads:
+                raise RuntimeError("Maximum number of threads reached")
+
+            t = threading.Thread(target=callback, args=args, kwargs=kwargs)
+            t.args = args
+            t.kw = kwargs
+
+            self.threads.append(t)
+
+        t.start()
+        return Thread(t, group=self)
+
+    def thread_done(self, thread):
+        with self._lock:
+            try:
+                self.threads.remove(
+                    thread.thread if isinstance(thread, Thread) else thread)
+            except ValueError:
+                pass
+
+    def timer_done(self, timer):
+        with self._lock:
+            try:
+                self.timers.remove(timer)
+            except ValueError:
+                pass
+
+    def cancel(self, *throw_args, **kwargs):
+        pass
+
+    def stop_timers(self):
+        with self._lock:
+
+            for timer in self.timers:
+                timer.stop()
+
+            self.timers = []
+
+    def stop(self, graceful=False):
+        self.stop_timers()
+
+        if graceful:
+            self._wait_threads()
+        else:
+            self._stop_threads()
+            time.sleep(0.05)
+
+    def _stop_threads(self):
+        current = threading.current_thread()
+
+        with self._lock:
+            for t in self.threads:
+                if t is not current and hasattr(t, "abort"):
+                    t.abort.set()
+
+            self.threads = [t for t in self.threads if t is current]
+
+    def _wait_threads(self):
+        current = threading.current_thread()
+
+        with self._lock:
+            for t in self.threads:
+                if t is not current:
+                    try:
+                        t.join()
+                    except Exception:
+                        LOG.exception('Error waiting on thread.')
+
+            self.threads = [t for t in self.threads if t is current]
+
+    def waitall(self):
+        """Block until all timers and threads in the group are complete.
+
+        """
+        with self._lock:
+            threads_copy = list(self.threads)
+            timers_copy = list(self.timers)
+
+        for t in threads_copy:
+            t.join()
+
+        for timer in timers_copy:
+            timer.wait()
+
+    # NOTE(tkajinam): To keep interface consistent with eventlet version
+    wait = waitall
+
+    def _set_attr(self, obj, attr, value):
+        try:
+            object.__setattr__(obj, attr, value)
+        except Exception:
+            if hasattr(obj, '__dict__'):
+                obj.__dict__[attr] = value
diff -pruN 4.1.1-2/oslo_service/tests/backend/tests/test_backend_init.py 4.2.2-0ubuntu1/oslo_service/tests/backend/tests/test_backend_init.py
--- 4.1.1-2/oslo_service/tests/backend/tests/test_backend_init.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/tests/backend/tests/test_backend_init.py	2025-07-10 09:42:42.000000000 +0000
@@ -35,34 +35,38 @@ class TestBackend(unittest.TestCase):
         self.assertEqual(backend.__class__.__name__, "EventletBackend")
 
     def test_init_backend_explicit(self):
-        """test that init_backend() can be called before get_backend()"""
-
+        """Test that init_backend() can be called before get_backend()."""
         init_backend(BackendType.EVENTLET)
-
         backend = get_backend()
         self.assertEqual(backend.__class__.__name__, "EventletBackend")
 
     def test_dont_reinit_backend_from_default(self):
-        """test that init_backend() can't be called after get_backend()"""
-
+        """Fail if init_backend() called after get_backend() with another."""
         get_backend()
-
         with self.assertRaisesRegex(
             exceptions.BackendAlreadySelected,
-            "The 'eventlet' backend is already set up",
+            "Backend already set to 'eventlet'",
         ):
-            init_backend(BackendType.EVENTLET)
+            init_backend(BackendType.THREADING)
 
     def test_dont_reinit_backend_explicit_init(self):
-        """test that init_backend() can't be called twice"""
-
+        """Fail if init_backend() called twice with different backend."""
         init_backend(BackendType.EVENTLET)
-
         with self.assertRaisesRegex(
             exceptions.BackendAlreadySelected,
-            "The 'eventlet' backend is already set up",
+            "Backend already set to 'eventlet'",
         ):
+            init_backend(BackendType.THREADING)
+
+    def test_reinit_backend_same_type_is_noop(self):
+        """init_backend() with same type is a no-op."""
+        init_backend(BackendType.EVENTLET)
+        try:
             init_backend(BackendType.EVENTLET)
+        except exceptions.BackendAlreadySelected:
+            self.fail(
+                "init_backend() should be a no-op if same type is passed"
+            )
 
     def test_cached_backend(self):
         """Test backend is cached after initial load."""
@@ -82,13 +86,46 @@ class TestBackend(unittest.TestCase):
 
     def test_backend_components(self):
         """Test that components are cached when init_backend is called."""
-
         init_backend(BackendType.EVENTLET)
-
         backend = get_backend()
-
         self.assertTrue(
             {"ServiceBase", "ServiceLauncher"}.intersection(
                 backend.get_service_components()
             )
         )
+
+    def test_get_backend_type(self):
+        """Ensure get_backend_type() returns the selected backend."""
+        self.assertIsNone(backend_module.get_backend_type())
+        init_backend(BackendType.THREADING)
+        self.assertEqual(
+            backend_module.get_backend_type(), BackendType.THREADING)
+
+
+class TestBackendHook(unittest.TestCase):
+    def setUp(self):
+        super().setUp()
+        backend_module._reset_backend()
+
+    def test_hook_sets_default_backend_when_not_explicitly_initialized(self):
+        backend_module.register_backend_default_hook(
+            lambda: BackendType.THREADING)
+        result = backend_module.get_backend()
+        self.assertEqual(
+            backend_module._cached_backend_type, BackendType.THREADING)
+        self.assertIsNotNone(result)
+        self.assertIsNotNone(result.get_service_components())
+
+    def test_hook_is_ignored_if_backend_already_initialized(self):
+        backend_module.init_backend(BackendType.EVENTLET)
+        backend_module.register_backend_default_hook(
+            lambda: BackendType.THREADING)
+        self.assertEqual(
+            backend_module._cached_backend_type, BackendType.EVENTLET)
+
+    def test_second_init_backend_raises_exception_even_with_hook(self):
+        backend_module.init_backend(BackendType.THREADING)
+        backend_module.register_backend_default_hook(
+            lambda: BackendType.EVENTLET)
+        with self.assertRaises(exceptions.BackendAlreadySelected):
+            backend_module.init_backend(BackendType.EVENTLET)
diff -pruN 4.1.1-2/oslo_service/tests/backend/threading/test_launcher.py 4.2.2-0ubuntu1/oslo_service/tests/backend/threading/test_launcher.py
--- 4.1.1-2/oslo_service/tests/backend/threading/test_launcher.py	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/tests/backend/threading/test_launcher.py	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,71 @@
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from unittest import mock
+from unittest import TestCase
+
+from oslo_config import cfg
+
+from oslo_service.backend.threading import service
+
+
+class DummyService(service.ServiceBase):
+    def start(self):
+        pass
+
+    def stop(self, graceful=False):
+        pass
+
+    def wait(self):
+        pass
+
+    def reset(self):
+        pass
+
+
+class ProcessLauncherTestCase(TestCase):
+    def setUp(self):
+        super().setUp()
+        self.conf = cfg.ConfigOpts()
+
+    def test_accepts_wait_interval_and_logs_warning(self):
+        # Patch the actual logger used in the module
+        with mock.patch('warnings.warn') as mock_warn:
+            launcher = service.ProcessLauncher(self.conf, wait_interval=0.1)
+            self.assertIsInstance(launcher, service.ProcessLauncher)
+
+            # Ensure warning is logged
+            mock_warn.assert_called_once()
+            self.assertIn('wait_interval', mock_warn.call_args[0][0])
+
+    def test_no_warning_without_wait_interval(self):
+        with mock.patch('warnings.warn') as mock_warn:
+            launcher = service.ProcessLauncher(self.conf)
+            self.assertIsInstance(launcher, service.ProcessLauncher)
+            mock_warn.assert_not_called()
+
+    def test_allows_multiple_launch_service_calls(self):
+        launcher = service.ProcessLauncher(self.conf)
+
+        s1 = DummyService()
+        s2 = DummyService()
+
+        # Just check that both calls do not raise
+        try:
+            launcher.launch_service(s1)
+            launcher.launch_service(s2)
+        except Exception as e:
+            self.fail(
+                f"Multiple launch_service() calls raised an exception: {e}")
diff -pruN 4.1.1-2/oslo_service/tests/test_service.py 4.2.2-0ubuntu1/oslo_service/tests/test_service.py
--- 4.1.1-2/oslo_service/tests/test_service.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/tests/test_service.py	2025-07-10 09:42:42.000000000 +0000
@@ -21,6 +21,7 @@ import multiprocessing
 import os
 import signal
 import socket
+import subprocess
 import time
 import traceback
 from unittest import mock
@@ -160,12 +161,14 @@ class ServiceLauncherTest(ServiceTestBas
         return workers
 
     def _get_workers(self):
-        f = os.popen('ps ax -o pid,ppid,command')
+        proc = subprocess.Popen(
+            ['ps', 'ax', '-o', 'pid,ppid,command'],
+            stdout=subprocess.PIPE, encoding='utf-8')
         # Skip ps header
-        f.readline()
+        proc.stdout.readline()
 
         processes = [tuple(int(p) for p in line.strip().split()[:2])
-                     for line in f]
+                     for line in proc.stdout]
         return [p for p, pp in processes if pp == self.pid]
 
     def test_killed_worker_recover(self):
diff -pruN 4.1.1-2/oslo_service/wsgi.py 4.2.2-0ubuntu1/oslo_service/wsgi.py
--- 4.1.1-2/oslo_service/wsgi.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/oslo_service/wsgi.py	2025-07-10 09:42:42.000000000 +0000
@@ -37,18 +37,6 @@ from oslo_service import sslutils
 
 from debtcollector import removals
 
-removals.removed_module(
-    __name__,
-    replacement="uwsgi",
-    removal_version="2026.2",
-    message=(
-        "The 'oslo_service.wsgi' module is deprecated and will be removed in "
-        "version 2026.2. We recommend transitioning to 'uwsgi' for serving "
-        "WSGI applications."
-    )
-)
-
-
 LOG = logging.getLogger(__name__)
 
 
@@ -67,6 +55,15 @@ class InvalidInput(Exception):
                 "Unexpected argument for periodic task creation: %(arg)s.")
 
 
+@removals.removed_class(
+    'Server',
+    removal_version="2026.2",
+    message=(
+        "The Server class utilises the eventlet service, and eventlet support "
+        "is deprecated for removal. You should remove use of eventlet servers "
+        "from your application and switch to deploying via WSGI."
+    )
+)
 class Server(service.ServiceBase):
     """Server class to manage a WSGI server, serving a WSGI application."""
 
diff -pruN 4.1.1-2/pyproject.toml 4.2.2-0ubuntu1/pyproject.toml
--- 4.1.1-2/pyproject.toml	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/pyproject.toml	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["pbr>=6.1.1"]
+build-backend = "pbr.build"
diff -pruN 4.1.1-2/releasenotes/notes/accept-wait-interval-threading-fc02f76056cbaed4.yaml 4.2.2-0ubuntu1/releasenotes/notes/accept-wait-interval-threading-fc02f76056cbaed4.yaml
--- 4.1.1-2/releasenotes/notes/accept-wait-interval-threading-fc02f76056cbaed4.yaml	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/releasenotes/notes/accept-wait-interval-threading-fc02f76056cbaed4.yaml	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,12 @@
+---
+fixes:
+  - |
+    The ``threading`` backend now accepts the ``wait_interval`` argument
+    in ``ProcessLauncher`` for compatibility with services like Neutron.
+    The parameter is still used in the ``eventlet`` backend to reduce CPU usage
+    during child monitoring loops, but has no effect in ``threading``.
+    A warning is logged if it is passed.
+    In addition, multiple calls to ``launch_service()`` on the same
+    ``ProcessLauncher`` instance are now supported, using a single
+    ``ServiceManager`` internally. This makes the threading backend fully
+    compatible with how the ``eventlet`` backend behaves.
\ No newline at end of file
diff -pruN 4.1.1-2/releasenotes/notes/add-threading-backend-9b0e601e5c1282e1.yaml 4.2.2-0ubuntu1/releasenotes/notes/add-threading-backend-9b0e601e5c1282e1.yaml
--- 4.1.1-2/releasenotes/notes/add-threading-backend-9b0e601e5c1282e1.yaml	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/releasenotes/notes/add-threading-backend-9b0e601e5c1282e1.yaml	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,64 @@
+features:
+  - |
+    Added a new `threading` backend to `oslo.service`, using standard Python
+    threads instead of `eventlet`. This includes a full implementation of:
+
+    - `Service`, `Launcher`, `ServiceLauncher`, and `ProcessLauncher` using
+      `cotyledon`
+    - `LoopingCall` variants (`FixedIntervalLoopingCall`,
+      `DynamicLoopingCall`, etc.) using `futurist.ThreadPoolExecutor`
+    - A new `ThreadGroup` and `Thread` abstraction to mimic `eventlet`-like
+      behavior
+    - A native `SignalHandler` implementation using standard Python signals
+
+    This backend provides a standard-thread-compatible alternative that avoids
+    monkey-patching, making it suitable for environments where `eventlet` is
+    problematic or undesirable.
+
+    Additionally:
+
+    - `ProcessLauncher` now supports a `no_fork=True` mode, allowing services
+      to run in the main process without forking. This is useful when `fork()`
+      is unsafe — for example, in threaded environments or with Python 3.12+
+      where the default multiprocessing start method has changed to `spawn`.
+
+    - A new `register_backend_default_hook()` API has been added. It allows
+      users to define a fallback backend type in case `init_backend()` was not
+      called early enough. This is helpful in environments where import order
+      or initialization timing cannot be guaranteed.
+
+      Example:
+
+      ```python
+      from oslo_service import backend
+      backend.register_backend_default_hook(lambda: backend.BackendType.THREADING)
+      ```
+
+      This hook will only be used if `init_backend()` has not already been called.
+
+upgrade:
+  - |
+    While Python 3.14 defaults to ``spawn`` as the multiprocessing start
+    method, `oslo.service` continues to rely on ``fork`` as the only supported
+    method for creating worker processes. Many parts of OpenStack depend on
+    objects that cannot be safely pickled (e.g. argparse parsers, thread locks,
+    lambdas in config defaults), which makes ``spawn`` currently impractical.
+
+    In the long term, process scaling should be handled externally (e.g. via
+    Kubernetes or systemd), rather than by the service itself.
+
+issues:
+  - |
+    When using the `threading` backend with multiple workers
+    (i.e. `ProcessLauncher` with `fork()`), **starting threads before the fork
+    occurs can lead to corrupted state** due to how `os.fork()` behaves in
+    multi-threaded processes. This is a known limitation in Python.
+
+    See: https://gibizer.github.io/posts/Eventlet-Removal-The-First-Threading-Bug/#threads--osfork--confused-threadpools
+
+    To avoid this issue, you can:
+
+    - Ensure that no threads are started before `oslo.service` forks the workers.
+    - Use `ProcessLauncher(no_fork=True)` to disable forking entirely.
+    - Explicitly manage thread lifecycle — for example, stop all threads before
+      forking, as currently done in Nova.
diff -pruN 4.1.1-2/releasenotes/notes/fix-threading-shutdown-cotyledon-3aff6ca0eb0125f5.yaml 4.2.2-0ubuntu1/releasenotes/notes/fix-threading-shutdown-cotyledon-3aff6ca0eb0125f5.yaml
--- 4.1.1-2/releasenotes/notes/fix-threading-shutdown-cotyledon-3aff6ca0eb0125f5.yaml	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/releasenotes/notes/fix-threading-shutdown-cotyledon-3aff6ca0eb0125f5.yaml	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,8 @@
+---
+fixes:
+  - |
+    Fixed a crash that occurred during service shutdown in the threading
+    backend, caused by an incorrect call to the non-existent internal method
+    ``_terminate()`` on ``cotyledon.ServiceManager``. The shutdown process now
+    correctly uses the public ``shutdown()`` method, ensuring graceful and
+    reliable termination of services.
\ No newline at end of file
diff -pruN 4.1.1-2/releasenotes/notes/undeprecate-wsgi-7e5306403625b8c6.yaml 4.2.2-0ubuntu1/releasenotes/notes/undeprecate-wsgi-7e5306403625b8c6.yaml
--- 4.1.1-2/releasenotes/notes/undeprecate-wsgi-7e5306403625b8c6.yaml	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/releasenotes/notes/undeprecate-wsgi-7e5306403625b8c6.yaml	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    The ``oslo_service.wsgi`` module has been undeprecated. It contains many
+    utilities besides the eventlet-based ``Server`` class. Instead, only the
+    ``Server`` class itself is now deprecated.
diff -pruN 4.1.1-2/releasenotes/source/2025.1.rst 4.2.2-0ubuntu1/releasenotes/source/2025.1.rst
--- 4.1.1-2/releasenotes/source/2025.1.rst	1970-01-01 00:00:00.000000000 +0000
+++ 4.2.2-0ubuntu1/releasenotes/source/2025.1.rst	2025-07-10 09:42:42.000000000 +0000
@@ -0,0 +1,6 @@
+===========================
+2025.1 Series Release Notes
+===========================
+
+.. release-notes::
+   :branch: stable/2025.1
diff -pruN 4.1.1-2/releasenotes/source/index.rst 4.2.2-0ubuntu1/releasenotes/source/index.rst
--- 4.1.1-2/releasenotes/source/index.rst	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/releasenotes/source/index.rst	2025-07-10 09:42:42.000000000 +0000
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   2025.1
    2024.2
    2024.1
    2023.2
diff -pruN 4.1.1-2/setup.cfg 4.2.2-0ubuntu1/setup.cfg
--- 4.1.1-2/setup.cfg	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/setup.cfg	2025-07-10 09:43:35.980527200 +0000
@@ -1,37 +1,47 @@
 [metadata]
 name = oslo.service
 summary = oslo.service library
-description_file =
-    README.rst
+description_file = 
+	README.rst
 author = OpenStack
 author_email = openstack-discuss@lists.openstack.org
 home_page = https://docs.openstack.org/oslo.service/latest/
 python_requires = >=3.9
-classifier =
-    Environment :: OpenStack
-    Intended Audience :: Information Technology
-    Intended Audience :: System Administrators
-    License :: OSI Approved :: Apache Software License
-    Operating System :: POSIX :: Linux
-    Programming Language :: Python
-    Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.9
-    Programming Language :: Python :: 3.10
-    Programming Language :: Python :: 3.11
-    Programming Language :: Python :: 3.12
-    Programming Language :: Python :: 3 :: Only
-    Programming Language :: Python :: Implementation :: CPython
+classifier = 
+	Environment :: OpenStack
+	Intended Audience :: Information Technology
+	Intended Audience :: System Administrators
+	License :: OSI Approved :: Apache Software License
+	Operating System :: POSIX :: Linux
+	Programming Language :: Python
+	Programming Language :: Python :: 3
+	Programming Language :: Python :: 3.9
+	Programming Language :: Python :: 3.10
+	Programming Language :: Python :: 3.11
+	Programming Language :: Python :: 3.12
+	Programming Language :: Python :: 3 :: Only
+	Programming Language :: Python :: Implementation :: CPython
+
+[options.extras_require]
+threading = 
+	cotyledon>=2.0.0
+	futurist>=3.1.1
 
 [files]
-packages =
-    oslo_service
+packages = 
+	oslo_service
 
 [entry_points]
-oslo.config.opts =
-    oslo.service.periodic_task = oslo_service.periodic_task:list_opts
-    oslo.service.service = oslo_service.service:list_opts
-    oslo.service.sslutils = oslo_service.sslutils:list_opts
-    oslo.service.wsgi = oslo_service.wsgi:list_opts
+oslo.config.opts = 
+	oslo.service.periodic_task = oslo_service.periodic_task:list_opts
+	oslo.service.service = oslo_service.service:list_opts
+	oslo.service.sslutils = oslo_service.sslutils:list_opts
+	oslo.service.wsgi = oslo_service.wsgi:list_opts
 
 [upload_sphinx]
 upload-dir = doc/build/html
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff -pruN 4.1.1-2/setup.py 4.2.2-0ubuntu1/setup.py
--- 4.1.1-2/setup.py	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/setup.py	2025-07-10 09:42:42.000000000 +0000
@@ -15,6 +15,4 @@
 
 import setuptools
 
-setuptools.setup(
-    setup_requires=['pbr>=2.0.0'],
-    pbr=True)
+setuptools.setup(setup_requires=['pbr>=2.0.0'], pbr=True)
diff -pruN 4.1.1-2/test-requirements.txt 4.2.2-0ubuntu1/test-requirements.txt
--- 4.1.1-2/test-requirements.txt	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/test-requirements.txt	2025-07-10 09:42:42.000000000 +0000
@@ -4,3 +4,5 @@ requests>=2.14.2 # Apache-2.0
 stestr>=2.0.0 # Apache-2.0
 
 coverage>=4.0 # Apache-2.0
+cotyledon>=2.0.0
+futurist>=3.1.1
diff -pruN 4.1.1-2/tox.ini 4.2.2-0ubuntu1/tox.ini
--- 4.1.1-2/tox.ini	2025-02-25 02:02:09.000000000 +0000
+++ 4.2.2-0ubuntu1/tox.ini	2025-07-10 09:42:42.000000000 +0000
@@ -4,23 +4,22 @@ envlist = py3,pep8
 
 [testenv]
 setenv =
-    VIRTUAL_ENV={envdir}
-    BRANCH_NAME=master
-    CLIENT_NAME=oslo.service
+  BRANCH_NAME=master
+  CLIENT_NAME=oslo.service
 deps =
   -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/test-requirements.txt
   -r{toxinidir}/requirements.txt
 allowlist_externals = find
 commands =
-    find . -type f -name "*.py[co]" -delete
-    stestr run --slowest {posargs}
+  find . -type f -name "*.py[co]" -delete
+  stestr run --slowest {posargs}
 
 [testenv:pep8]
 deps =
-    pre-commit
+  pre-commit
 commands =
-    pre-commit run -a
+  pre-commit run -a
 
 [testenv:venv]
 commands = {posargs}
