• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bernardladenthin / BitcoinAddressFinder / #315

23 May 2025 03:19PM UTC coverage: 66.005% (-1.9%) from 67.925%
#315

push

bernardladenthin
Refactor OpenCL resource handling and improve consistency across API

- Introduced AutoCloseable for OpenCL resources (OpenClTask, OpenCLContext)
- Replaced manual release() methods with try-with-resources using close()
- Added isClosed() for defensive resource lifecycle checks
- Refactored buffer handling to separate host and device pointers
- Renamed createSecrets() param for clarity (overallWorkSize instead of batchSizeInBits)
- Updated all tests and affected logic to match new API and lifecycle
- Enhanced OpenCLGridResult to avoid unnecessary buffer duplication
- Improved benchmark documentation in README with CPU column
- Added optional LMDB in-memory caching in configuration

11 of 118 new or added lines in 10 files covered. (9.32%)

3 existing lines in 2 files now uncovered.

1231 of 1865 relevant lines covered (66.01%)

0.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

24.53
/src/main/java/net/ladenthin/bitcoinaddressfinder/ProducerOpenCL.java
1
// @formatter:off
2
/**
3
 * Copyright 2020 Bernard Ladenthin bernard.ladenthin@gmail.com
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *    http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
// @formatter:on
19
package net.ladenthin.bitcoinaddressfinder;
20

21
import com.google.common.annotations.VisibleForTesting;
22
import java.io.IOException;
23
import java.math.BigInteger;
24
import java.util.concurrent.Executors;
25
import java.util.concurrent.ThreadPoolExecutor;
26
import net.ladenthin.bitcoinaddressfinder.configuration.CProducerOpenCL;
27
import org.jspecify.annotations.Nullable;
28
import org.slf4j.Logger;
29
import org.slf4j.LoggerFactory;
30

31
public class ProducerOpenCL extends AbstractProducer {
32

33
    private final CProducerOpenCL producerOpenCL;
34

35
    @VisibleForTesting
36
    final ThreadPoolExecutor resultReaderThreadPoolExecutor;
37
    @VisibleForTesting
38
    @Nullable
39
    OpenCLContext openCLContext;
40

41
    public ProducerOpenCL(CProducerOpenCL producerOpenCL, Consumer consumer, KeyUtility keyUtility, KeyProducer keyProducer, BitHelper bitHelper) {
42
        super(producerOpenCL, consumer, keyUtility, keyProducer, bitHelper);
1✔
43
        this.producerOpenCL = producerOpenCL;
1✔
44
        this.resultReaderThreadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(producerOpenCL.maxResultReaderThreads);
1✔
45
        if (false) {
46
            int prestartedThreads = resultReaderThreadPoolExecutor.prestartAllCoreThreads();
47
            if (prestartedThreads != producerOpenCL.maxResultReaderThreads) {
48
                throw new RuntimeException("Unable to prestart core threads.");
49
            }
50
        }
51
    }
1✔
52

53
    @Override
54
    public void initProducer() {
55
        super.initProducer();
×
56
        openCLContext = new OpenCLContext(producerOpenCL, bitHelper);
×
57
        try {
58
            openCLContext.init();
×
59
        } catch (IOException e) {
×
60
            throw new RuntimeException(e);
×
61
        }
×
62
    }
×
63

64
    @Override
65
    public void processSecretBase(BigInteger secretBase) {
66
        if (openCLContext == null) {
1✔
67
            throw new IllegalStateException("ProducerOpenCL not initialized");
1✔
68
        }
69
        try {
70
            waitTillFreeThreadsInPool();
×
71
            if(getLogger().isDebugEnabled()) {
×
72
                getLogger().debug("openCLContext.createKeys for secretBase: " + secretBase);
×
73
            }
74
            OpenCLGridResult openCLGridResult = openCLContext.createKeys(secretBase);
×
75
            ResultReaderRunnable resultReaderRunnable = new ResultReaderRunnable(openCLGridResult, consumer, secretBase, this);
×
76

77
            if(getLogger().isDebugEnabled()) {
×
78
                getLogger().debug("submit resultReaderRunnable for secretBase: " + secretBase);
×
79
            }
80
            resultReaderThreadPoolExecutor.submit(resultReaderRunnable, openCLContext);
×
81
        } catch (Exception e) {
×
82
            logErrorInProduceKeys(e, secretBase);
×
83
        }
×
84
    }
×
85

86
    @Override
87
    public void processSecrets(BigInteger[] secrets) {
88
        throw new UnsupportedOperationException("Not supported yet.");
×
89
    }
90
    
91
    protected static class ResultReaderRunnable implements Runnable {
92
        
93
        private Logger logger = LoggerFactory.getLogger(this.getClass());
×
94
        
95
        private final OpenCLGridResult openCLGridResult;
96
        private final Consumer consumer;
97
        private final BigInteger secretBase;
98
        private final AbstractProducer abstractProducer;
99
        
100
        ResultReaderRunnable(OpenCLGridResult openCLGridResult, Consumer consumer, BigInteger secretBase, AbstractProducer abstractProducer) {
×
101
            this.openCLGridResult = openCLGridResult;
×
102
            this.consumer = consumer;
×
103
            this.secretBase = secretBase;
×
104
            this.abstractProducer = abstractProducer;
×
105
        }
×
106

107
        @Override
108
        public void run() {
109
            logger.trace("ResultReaderRunnable started");
×
110
            try {
111
                PublicKeyBytes[] publicKeyBytesArray = openCLGridResult.getPublicKeyBytes();
×
112
                openCLGridResult.freeResult();
×
113
                consumer.consumeKeys(publicKeyBytesArray);
×
114
            } catch (Throwable e) {
×
115
                abstractProducer.logErrorInProduceKeys(e, secretBase);
×
116
            }
×
117
            logger.trace("ResultReaderRunnable finished");
×
118
        }
×
119
    }
120
    
121
    @VisibleForTesting
122
    void waitTillFreeThreadsInPool() throws InterruptedException {
123
        while(getFreeThreads() < 1) {
1✔
124
            Thread.sleep(producerOpenCL.delayBlockedReader);
×
125
            getLogger().trace("No possible free threads to read OpenCL results. May increase maxResultReaderThreads.");
×
126
        }
127
    }
1✔
128

129
    @VisibleForTesting
130
    int getFreeThreads() {
131
        return resultReaderThreadPoolExecutor.getMaximumPoolSize() - resultReaderThreadPoolExecutor.getActiveCount();
1✔
132
    }
133

134
    @Override
135
    public void releaseProducer() {
136
        super.releaseProducer();
1✔
137
        resultReaderThreadPoolExecutor.shutdown();
1✔
138
        if (openCLContext != null) {
1✔
NEW
139
            openCLContext.close();
×
140
            openCLContext = null;
×
141
        }
142
    }
1✔
143

144
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc