Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
@@ -0,0 +1 @@
dist
+2
View File
@@ -0,0 +1,2 @@
builds:
- skip: true
+28
View File
@@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2025, Kovid Goyal
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+5
View File
@@ -0,0 +1,5 @@
# go-parallel
Utility functions to make running code in parallel easier and safer.
Panics in go routines are turned into regular errors, instead of crashing
the program.
+219
View File
@@ -0,0 +1,219 @@
package parallel
import (
"fmt"
"iter"
"runtime"
"slices"
"strings"
"sync"
"sync/atomic"
)
var _ = fmt.Print
type PanicError struct {
frames []runtime.Frame
panic_value any
}
const indent_lead = " "
func format_frame_line(frame runtime.Frame) string {
return fmt.Sprintf("\r\n%s%s%s:%d", indent_lead, frame.Function, frame.File, frame.Line)
}
func (e *PanicError) walk(level int, yield func(string) bool) bool {
s := "Panic"
cause := fmt.Sprintf("%v", e.panic_value)
if _, ok := e.panic_value.(*PanicError); ok {
cause = "sub-panic (see below)"
}
if level > 0 {
s = "\r\n--> Sub-panic"
}
if !yield(fmt.Sprintf("%s caused by: %s\r\nStack trace (most recent call first):", s, cause)) {
return false
}
for _, f := range e.frames {
if !yield(format_frame_line(f)) {
return false
}
}
if sp, ok := e.panic_value.(*PanicError); ok {
return sp.walk(level+1, yield)
}
return true
}
func (e *PanicError) lines() iter.Seq[string] {
return func(yield func(string) bool) {
e.walk(0, yield)
}
}
func (e *PanicError) Error() string {
return strings.Join(slices.Collect(e.lines()), "")
}
func (e *PanicError) Unwrap() error {
if ans, ok := e.panic_value.(*PanicError); ok {
return ans
}
return nil
}
// Format a stack trace on panic and return it as an error
func Format_stacktrace_on_panic(r any, skip_frames int) (err *PanicError) {
pcs := make([]uintptr, 512)
n := runtime.Callers(2+skip_frames, pcs)
var ans []runtime.Frame
frames := runtime.CallersFrames(pcs[:n])
found_first_frame := false
for frame, more := frames.Next(); more; frame, more = frames.Next() {
if !found_first_frame {
if strings.HasPrefix(frame.Function, "runtime.") {
continue
}
found_first_frame = true
}
ans = append(ans, frame)
}
return &PanicError{frames: ans, panic_value: r}
}
// Run the specified function in parallel over chunks from the specified range.
// If the function panics, it is turned into a regular error. If multiple function calls panic,
// any one of the panics will be returned.
func Run_in_parallel_over_range(num_procs int, f func(int, int), start, limit int) (err error) {
num_items := limit - start
if num_procs <= 0 {
num_procs = runtime.GOMAXPROCS(0)
}
num_procs = max(1, min(num_procs, num_items))
if num_procs < 2 {
defer func() {
if r := recover(); r != nil {
err = Format_stacktrace_on_panic(r, 1)
}
}()
f(start, limit)
return
}
err_once := sync.Once{}
chunk_sz := max(1, num_items/num_procs)
var wg sync.WaitGroup
for start < limit {
end := min(start+chunk_sz, limit)
wg.Add(1)
go func(start, end int) {
defer func() {
if r := recover(); r != nil {
perr := Format_stacktrace_on_panic(r, 1)
err_once.Do(func() { err = perr })
}
wg.Done()
}()
f(start, end)
}(start, end)
start = end
}
wg.Wait()
return
}
// Run the specified function in parallel over chunks from the specified range.
// If the function panics, it is turned into a regular error. If the function
// returns an error it is returned. If multiple function calls panic or return errors,
// any one of them will be returned.
func Run_in_parallel_over_range_with_error(num_procs int, f func(int, int) error, start, limit int) (err error) {
num_items := limit - start
if num_procs <= 0 {
num_procs = runtime.GOMAXPROCS(0)
}
num_procs = max(1, min(num_procs, num_items))
if num_procs < 2 {
defer func() {
if r := recover(); r != nil {
err = Format_stacktrace_on_panic(r, 1)
}
}()
err = f(start, limit)
return
}
chunk_sz := max(1, num_items/num_procs)
var wg sync.WaitGroup
err_once := sync.Once{}
for start < limit {
end := min(start+chunk_sz, limit)
wg.Add(1)
go func(start, end int) {
defer func() {
if r := recover(); r != nil {
perr := Format_stacktrace_on_panic(r, 1)
err_once.Do(func() { err = perr })
}
wg.Done()
}()
if cerr := f(start, end); cerr != nil {
err_once.Do(func() { err = cerr })
}
}(start, end)
start = end
}
wg.Wait()
return
}
// Run the specified function in parallel until one of them returns true.
// The functions are passed a keep_going variable that they should periodically check and return false if it is false.
// If any of the functions panic, the panic is turned into a regular error and returned.
func Run_in_parallel_to_first_result(num_procs int, f func(start, limit int, keep_going *atomic.Bool) bool, start, limit int) (err error) {
var keep_going atomic.Bool
keep_going.Store(true)
num_items := limit - start
if num_procs <= 0 {
num_procs = runtime.GOMAXPROCS(0)
}
num_procs = max(1, min(num_procs, num_items))
if num_procs < 2 {
defer func() {
if r := recover(); r != nil {
err = Format_stacktrace_on_panic(r, 1)
}
}()
f(start, limit, &keep_going)
return
}
chunk_sz := max(1, num_items/num_procs)
var wg sync.WaitGroup
var err_once sync.Once
ch := make(chan bool, num_items/chunk_sz+1)
for start < limit {
end := min(start+chunk_sz, limit)
wg.Add(1)
go func(start, end int) {
defer func() {
if r := recover(); r != nil {
perr := Format_stacktrace_on_panic(r, 1)
err_once.Do(func() { err = perr })
}
wg.Done()
}()
ch <- f(start, end, &keep_going)
}(start, end)
start = end
}
go func() {
wg.Wait()
close(ch)
}()
for x := range ch {
if x {
break
}
}
keep_going.Store(false)
wg.Wait()
return
}
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>
import os
import subprocess
VERSION = '1.1.1'
def run(*args: str):
cp = subprocess.run(args)
if cp.returncode != 0:
raise SystemExit(cp.returncode)
def main():
try:
ans = input(f'Publish version \033[91m{VERSION}\033[m (y/n): ')
except KeyboardInterrupt:
ans = 'n'
if ans.lower() != 'y':
return
os.environ['GITHUB_TOKEN'] = open(os.path.join(os.environ['PENV'], 'github-token')).read().strip().partition(':')[2]
run('git', 'tag', '-a', 'v' + VERSION, '-m', f'version {VERSION}')
run('git', 'push')
run('goreleaser', 'release', '--clean')
if __name__ == '__main__':
main()